feat: replace function node code editor with monaco

This commit is contained in:
Arpad Gabor 2021-10-23 18:38:46 +03:00
parent 37930057d8
commit c63f365a65
4 changed files with 97 additions and 44 deletions

View file

@ -27,6 +27,7 @@
"dependencies": { "dependencies": {
"@fontsource/open-sans": "^4.5.0", "@fontsource/open-sans": "^4.5.0",
"n8n-design-system": "~0.5.0", "n8n-design-system": "~0.5.0",
"monaco-editor": "^0.29.1",
"timeago.js": "^4.0.2", "timeago.js": "^4.0.2",
"v-click-outside": "^3.1.2", "v-click-outside": "^3.1.2",
"vue-fragment": "^1.5.2" "vue-fragment": "^1.5.2"
@ -72,11 +73,12 @@
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
"lodash.set": "^4.3.2", "lodash.set": "^4.3.2",
"n8n-workflow": "~0.73.0", "n8n-workflow": "~0.73.0",
"sass": "^1.26.5", "monaco-editor-webpack-plugin": "^5.0.0",
"normalize-wheel": "^1.0.1", "normalize-wheel": "^1.0.1",
"prismjs": "^1.17.1", "prismjs": "^1.17.1",
"quill": "^2.0.0-dev.3", "quill": "^2.0.0-dev.3",
"quill-autoformat": "^0.1.1", "quill-autoformat": "^0.1.1",
"sass": "^1.26.5",
"sass-loader": "^8.0.2", "sass-loader": "^8.0.2",
"string-template-parser": "^1.2.6", "string-template-parser": "^1.2.6",
"ts-jest": "^26.3.0", "ts-jest": "^26.3.0",

View file

@ -1,61 +1,107 @@
<template> <template>
<div v-if="dialogVisible"> <el-dialog
<el-dialog :visible="dialogVisible" append-to-body :close-on-click-modal="false" width="80%" :title="`Edit ${parameter.displayName}`" :before-close="closeDialog"> visible
append-to-body
:close-on-click-modal="false"
width="80%"
:title="`Edit ${parameter.displayName}`"
:before-close="closeDialog"
>
<div class="text-editor-wrapper ignore-key-press"> <div class="text-editor-wrapper ignore-key-press">
<div class="editor-description"> <div ref="code" class="text-editor" @keydown.stop></div>
{{parameter.displayName}}:
</div>
<div class="text-editor" @keydown.stop>
<prism-editor :lineNumbers="true" :code="value" :readonly="isReadOnly" @change="valueChanged" language="js"></prism-editor>
</div>
</div> </div>
</el-dialog> </el-dialog>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
// @ts-ignore // @ts-ignore
import PrismEditor from 'vue-prism-editor'; // import PrismEditor from 'vue-prism-editor';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
import { genericHelpers } from '@/components/mixins/genericHelpers'; import { genericHelpers } from '@/components/mixins/genericHelpers';
import mixins from 'vue-typed-mixins'; import mixins from 'vue-typed-mixins';
import { IExecutionResponse } from '@/Interface';
import { INodeExecutionData } from 'n8n-workflow';
export default mixins( export default mixins(genericHelpers).extend({
genericHelpers,
)
.extend({
name: 'CodeEdit', name: 'CodeEdit',
props: [ props: ['dialogVisible', 'parameter', 'value'],
'dialogVisible', data() {
'parameter',
'value',
],
components: {
PrismEditor,
},
data () {
return { return {
monacoInstance: null as monaco.editor.IStandaloneCodeEditor | null,
monacoLibrary: null as monaco.IDisposable | null,
}; };
}, },
methods: { mounted() {
valueChanged (value: string) { setTimeout(this.loadEditor);
this.$emit('valueChanged', value);
}, },
destroyed() {
closeDialog () { this.monacoLibrary!.dispose();
},
methods: {
closeDialog() {
// Handle the close externally as the visible parameter is an external prop // Handle the close externally as the visible parameter is an external prop
// and is so not allowed to be changed here. // and is so not allowed to be changed here.
this.$emit('closeDialog'); this.$emit('closeDialog');
return false; return false;
}, },
},
loadEditor() {
if (!this.$refs.code) return;
this.monacoInstance = monaco.editor.create(this.$refs.code as HTMLElement, {
value: this.value,
language: 'javascript',
tabSize: 2,
}); });
this.monacoInstance.onDidChangeModelContent((ev) => {
if (this.monacoInstance) {
const model = this.monacoInstance.getModel();
if (model) {
this.$emit('valueChanged', model.getValue());
}
}
});
this.loadAutocompleteData();
},
loadAutocompleteData(): void {
const executedWorkflow: IExecutionResponse | null = this.$store.getters.getWorkflowExecution;
let autocompleteData: INodeExecutionData[] = [{ json: { myNewField: 2 } }];
if (executedWorkflow) {
const lastNodeExecuted = executedWorkflow.data.resultData.lastNodeExecuted;
if (lastNodeExecuted) {
const data = executedWorkflow.data.resultData.runData[lastNodeExecuted];
// @ts-ignore
autocompleteData = data[0].data!.main[0];
}
}
this.monacoLibrary = monaco.languages.typescript.javascriptDefaults.addExtraLib(
[
`/**\n\`\`\`\nconst items = ${JSON.stringify(autocompleteData, null, 2)}\n\`\`\`\n*/`,
`const items = ${JSON.stringify(autocompleteData)}`,
].join('\n'),
);
},
},
});
</script> </script>
<style scoped> <style scoped>
.editor-description { .editor-description {
font-weight: bold; font-weight: bold;
padding: 0 0 0.5em 0.2em;; padding: 0 0 0.5em 0.2em;
}
.text-editor {
min-height: 30rem;
} }
</style> </style>

View file

@ -13,7 +13,7 @@
/> />
<div v-else-if="['json', 'string'].includes(parameter.type) || remoteParameterOptionsLoadingIssues !== null"> <div v-else-if="['json', 'string'].includes(parameter.type) || remoteParameterOptionsLoadingIssues !== null">
<code-edit :dialogVisible="codeEditDialogVisible" :value="value" :parameter="parameter" @closeDialog="closeCodeEditDialog" @valueChanged="expressionUpdated"></code-edit> <code-edit v-if="codeEditDialogVisible" :value="value" :parameter="parameter" @closeDialog="closeCodeEditDialog" @valueChanged="expressionUpdated"></code-edit>
<text-edit :dialogVisible="textEditDialogVisible" :value="value" :parameter="parameter" @closeDialog="closeTextEditDialog" @valueChanged="expressionUpdated"></text-edit> <text-edit :dialogVisible="textEditDialogVisible" :value="value" :parameter="parameter" @closeDialog="closeTextEditDialog" @valueChanged="expressionUpdated"></text-edit>
<div v-if="isEditor === true" class="clickable" @click="displayEditDialog()"> <div v-if="isEditor === true" class="clickable" @click="displayEditDialog()">

View file

@ -1,3 +1,5 @@
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = { module.exports = {
chainWebpack: config => config.resolve.symlinks(false), chainWebpack: config => config.resolve.symlinks(false),
// transpileDependencies: [ // transpileDependencies: [
@ -13,6 +15,9 @@ module.exports = {
devServer: { devServer: {
disableHostCheck: true, disableHostCheck: true,
}, },
plugins: [
new MonacoWebpackPlugin(),
],
}, },
css: { css: {
loaderOptions: { loaderOptions: {