feat(editor): Create separate components for JS and JSON editors (no-changelog) (#8156)

## Summary
This is part-1 of refactoring our code editors to extract different type
of editors into their own components.
In part-2 we'll
1. delete a of unused or duplicate code
2. switch to a `useEditor` composable to bring more UX consistency
across all the code editors.

## Review / Merge checklist
- [x] PR title and summary are descriptive
- [x] Tests included
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2023-12-29 10:49:27 +01:00
committed by GitHub
parent 1286d6583c
commit 216ec079c9
31 changed files with 290 additions and 91 deletions

View File

@@ -0,0 +1,95 @@
<template>
<div ref="jsEditor" class="ph-no-capture js-editor"></div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { acceptCompletion, autocompletion } from '@codemirror/autocomplete';
import { indentWithTab, history, redo, toggleComment, undo } from '@codemirror/commands';
import { foldGutter, indentOnInput } from '@codemirror/language';
import { javascript } from '@codemirror/lang-javascript';
import { lintGutter } from '@codemirror/lint';
import type { Extension } from '@codemirror/state';
import { EditorState } from '@codemirror/state';
import type { ViewUpdate } from '@codemirror/view';
import {
dropCursor,
EditorView,
highlightActiveLine,
highlightActiveLineGutter,
keymap,
lineNumbers,
} from '@codemirror/view';
import { codeNodeEditorTheme } from '../CodeNodeEditor/theme';
export default defineComponent({
name: 'JsEditor',
props: {
modelValue: {
type: String,
required: true,
},
isReadOnly: {
type: Boolean,
default: false,
},
rows: {
type: Number,
default: 4,
},
},
data() {
return {
editor: null as EditorView | null,
editorState: null as EditorState | null,
};
},
computed: {
doc(): string {
return this.editor?.state.doc.toString() ?? '';
},
extensions(): Extension[] {
const { isReadOnly } = this;
const extensions: Extension[] = [
javascript(),
lineNumbers(),
EditorView.lineWrapping,
EditorState.readOnly.of(isReadOnly),
EditorView.editable.of(!isReadOnly),
codeNodeEditorTheme({ isReadOnly, customMinHeight: this.rows }),
];
if (!isReadOnly) {
extensions.push(
history(),
keymap.of([
{ key: 'Mod-z', run: undo },
{ key: 'Mod-Shift-z', run: redo },
{ key: 'Mod-/', run: toggleComment },
{ key: 'Tab', run: acceptCompletion },
indentWithTab,
]),
lintGutter(),
autocompletion(),
indentOnInput(),
highlightActiveLine(),
highlightActiveLineGutter(),
foldGutter(),
dropCursor(),
EditorView.updateListener.of((viewUpdate: ViewUpdate) => {
if (!viewUpdate.docChanged || !this.editor) return;
this.$emit('update:modelValue', this.editor?.state.doc.toString());
}),
);
}
return extensions;
},
},
mounted() {
const state = EditorState.create({ doc: this.modelValue, extensions: this.extensions });
const parent = this.$refs.jsEditor as HTMLDivElement;
this.editor = new EditorView({ parent, state });
this.editorState = this.editor.state;
},
});
</script>