Files
n8n-enterprise-unlocked/packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalInput.vue

117 lines
2.7 KiB
Vue

<template>
<div ref="root" :class="$style.editor" @keydown.stop></div>
</template>
<script setup lang="ts">
import { history } from '@codemirror/commands';
import { Prec } from '@codemirror/state';
import { dropCursor, EditorView, keymap } from '@codemirror/view';
import { computed, onMounted, ref, watch } from 'vue';
import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler';
import { n8nAutocompletion, n8nLang } from '@/plugins/codemirror/n8nLang';
import { forceParse } from '@/utils/forceParse';
import { completionStatus } from '@codemirror/autocomplete';
import { inputTheme } from './theme';
import { useExpressionEditor } from '@/composables/useExpressionEditor';
import {
autocompleteKeyMap,
enterKeyMap,
historyKeyMap,
tabKeyMap,
} from '@/plugins/codemirror/keymap';
import { infoBoxTooltips } from '@/plugins/codemirror/tooltips/InfoBoxTooltip';
import type { Segment } from '@/types/expressions';
import { removeExpressionPrefix } from '@/utils/expressions';
import { mappingDropCursor } from '@/plugins/codemirror/dragAndDrop';
type Props = {
modelValue: string;
path: string;
isReadOnly?: boolean;
};
const props = withDefaults(defineProps<Props>(), {
isReadOnly: false,
});
const emit = defineEmits<{
change: [value: { value: string; segments: Segment[] }];
focus: [];
close: [];
}>();
const root = ref<HTMLElement>();
const extensions = computed(() => [
inputTheme(props.isReadOnly),
Prec.highest(
keymap.of([
...tabKeyMap(),
...historyKeyMap,
...enterKeyMap,
...autocompleteKeyMap,
{
any: (view, event) => {
if (event.key === 'Escape' && completionStatus(view.state) === null) {
event.stopPropagation();
emit('close');
}
return false;
},
},
]),
),
n8nLang(),
n8nAutocompletion(),
mappingDropCursor(),
dropCursor(),
history(),
expressionInputHandler(),
EditorView.lineWrapping,
EditorView.domEventHandlers({ scroll: forceParse }),
infoBoxTooltips(),
]);
const editorValue = ref<string>(removeExpressionPrefix(props.modelValue));
const { segments, readEditorValue, editor, hasFocus, focus } = useExpressionEditor({
editorRef: root,
editorValue,
extensions,
isReadOnly: props.isReadOnly,
autocompleteTelemetry: { enabled: true, parameterPath: props.path },
});
watch(
() => props.modelValue,
(newValue) => {
editorValue.value = removeExpressionPrefix(newValue);
},
);
watch(segments.display, (newSegments) => {
emit('change', {
value: '=' + readEditorValue(),
segments: newSegments,
});
});
watch(hasFocus, (focused) => {
if (focused) {
emit('focus');
}
});
onMounted(() => {
focus();
});
defineExpose({ editor });
</script>
<style lang="scss" module>
:global(.cm-content) {
border-radius: var(--border-radius-base);
}
</style>