mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
✨ Improve Code-Editor in Function-Nodes
This commit is contained in:
parent
48ccb36536
commit
2b2113433f
|
@ -62,6 +62,7 @@
|
|||
"lodash.set": "^4.3.2",
|
||||
"n8n-workflow": "^0.10.0",
|
||||
"node-sass": "^4.12.0",
|
||||
"prismjs": "^1.17.1",
|
||||
"quill": "^2.0.0-dev.3",
|
||||
"quill-autoformat": "^0.1.1",
|
||||
"sass-loader": "^7.0.1",
|
||||
|
@ -72,6 +73,7 @@
|
|||
"vue": "^2.6.9",
|
||||
"vue-cli-plugin-webpack-bundle-analyzer": "^1.3.0",
|
||||
"vue-json-pretty": "^1.4.1",
|
||||
"vue-prism-editor": "^0.3.0",
|
||||
"vue-router": "^3.0.6",
|
||||
"vue-template-compiler": "^2.5.17",
|
||||
"vue-typed-mixins": "^0.1.0",
|
||||
|
|
60
packages/editor-ui/src/components/CodeEdit.vue
Normal file
60
packages/editor-ui/src/components/CodeEdit.vue
Normal file
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<div v-if="dialogVisible">
|
||||
<el-dialog :visible="dialogVisible" append-to-body width="80%" :title="`Edit ${parameter.displayName}`" :before-close="closeDialog">
|
||||
<div class="text-editor-wrapper ignore-key-press">
|
||||
<div class="editor-description">
|
||||
{{parameter.displayName}}:
|
||||
</div>
|
||||
<div class="text-editor" @keydown.stop>
|
||||
<prism-editor :lineNumbers="true" :code="value" @change="valueChanged" language="js"></prism-editor>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
// @ts-ignore
|
||||
import PrismEditor from 'vue-prism-editor';
|
||||
|
||||
import {
|
||||
Workflow,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'CodeEdit',
|
||||
props: [
|
||||
'dialogVisible',
|
||||
'parameter',
|
||||
'value',
|
||||
],
|
||||
components: {
|
||||
PrismEditor,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
valueChanged (value: string) {
|
||||
this.$emit('valueChanged', value);
|
||||
},
|
||||
|
||||
closeDialog () {
|
||||
// Handle the close externally as the visible parameter is an external prop
|
||||
// and is so not allowed to be changed here.
|
||||
this.$emit('closeDialog');
|
||||
return false;
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.editor-description {
|
||||
font-weight: bold;
|
||||
padding: 0 0 0.5em 0.2em;;
|
||||
}
|
||||
</style>
|
|
@ -1,12 +1,19 @@
|
|||
<template>
|
||||
<div @keydown.stop :class="parameterInputClasses">
|
||||
<expression-edit :dialogVisible="expressionEditDialogVisible" :value="value" :parameter="parameter" :path="path" @closeDialog="closeExpressionEditDialog" @valueChanged="expressionUpdated"></expression-edit>
|
||||
<text-edit :dialogVisible="textEditDialogVisible" :value="value" :parameter="parameter" @closeDialog="closeTextEditDialog" @valueChanged="expressionUpdated"></text-edit>
|
||||
<div class="parameter-input ignore-key-press" :style="parameterInputWrapperStyle">
|
||||
<el-input v-if="['json', 'string'].includes(parameter.type)" ref="inputField" size="small" :type="getStringInputType" :rows="getArgument('rows')" :value="displayValue" :disabled="isReadOnly" @change="valueChanged" @keydown.stop @focus="setFocus" :title="displayTitle" :placeholder="isValueExpression?'':parameter.placeholder">
|
||||
<font-awesome-icon v-if="!isValueExpression && !isReadOnly" slot="suffix" icon="external-link-alt" class="edit-window-button clickable" title="Open Edit Window" @click="textEditDialogVisible = true" />
|
||||
</el-input>
|
||||
<div v-if="['json', 'string'].includes(parameter.type)">
|
||||
<code-edit :dialogVisible="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>
|
||||
|
||||
<div v-if="isEditor === true" class="clickable" @click="displayEditDialog()">
|
||||
<prism-editor v-if="!codeEditDialogVisible" :lineNumbers="true" :readonly="true" :code="displayValue" language="js"></prism-editor>
|
||||
</div>
|
||||
|
||||
<el-input v-else ref="inputField" size="small" :type="getStringInputType" :rows="getArgument('rows')" :value="displayValue" :disabled="isReadOnly" @change="valueChanged" @keydown.stop @focus="setFocus" :title="displayTitle" :placeholder="isValueExpression?'':parameter.placeholder">
|
||||
<font-awesome-icon v-if="!isValueExpression && !isReadOnly" slot="suffix" icon="external-link-alt" class="edit-window-button clickable" title="Open Edit Window" @click="displayEditDialog()" />
|
||||
</el-input>
|
||||
</div>
|
||||
<div v-else-if="parameter.type === 'dateTime'">
|
||||
<el-date-picker
|
||||
v-model="tempValue"
|
||||
|
@ -109,7 +116,10 @@ import {
|
|||
Workflow,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import CodeEdit from '@/components/CodeEdit.vue';
|
||||
import ExpressionEdit from '@/components/ExpressionEdit.vue';
|
||||
// @ts-ignore
|
||||
import PrismEditor from 'vue-prism-editor';
|
||||
import TextEdit from '@/components/TextEdit.vue';
|
||||
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
||||
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
||||
|
@ -127,7 +137,9 @@ export default mixins(
|
|||
.extend({
|
||||
name: 'ParameterInput',
|
||||
components: {
|
||||
CodeEdit,
|
||||
ExpressionEdit,
|
||||
PrismEditor,
|
||||
TextEdit,
|
||||
},
|
||||
props: [
|
||||
|
@ -139,6 +151,7 @@ export default mixins(
|
|||
],
|
||||
data () {
|
||||
return {
|
||||
codeEditDialogVisible: false,
|
||||
nodeName: '',
|
||||
expressionAddOperation: 'set' as 'add' | 'set',
|
||||
expressionEditDialogVisible: false,
|
||||
|
@ -324,6 +337,9 @@ export default mixins(
|
|||
isDefault (): boolean {
|
||||
return this.parameter.default === this.value;
|
||||
},
|
||||
isEditor (): boolean {
|
||||
return this.getArgument('editor') === 'code';
|
||||
},
|
||||
isValueExpression () {
|
||||
if (this.parameter.noDataExpression === true) {
|
||||
return false;
|
||||
|
@ -401,12 +417,24 @@ export default mixins(
|
|||
|
||||
this.remoteParameterOptionsLoading = false;
|
||||
},
|
||||
closeCodeEditDialog () {
|
||||
this.codeEditDialogVisible = false;
|
||||
},
|
||||
closeExpressionEditDialog () {
|
||||
this.expressionEditDialogVisible = false;
|
||||
},
|
||||
closeTextEditDialog () {
|
||||
this.textEditDialogVisible = false;
|
||||
},
|
||||
displayEditDialog () {
|
||||
console.log('displayEditDialog...');
|
||||
|
||||
if (this.isEditor) {
|
||||
this.codeEditDialogVisible = true;
|
||||
} else {
|
||||
this.textEditDialogVisible = true;
|
||||
}
|
||||
},
|
||||
getArgument (argumentName: string): string | number | boolean | undefined {
|
||||
if (this.parameter.typeOptions === undefined) {
|
||||
return undefined;
|
||||
|
@ -431,7 +459,7 @@ export default mixins(
|
|||
}
|
||||
|
||||
if (this.parameter.type === 'string' && this.getArgument('alwaysOpenEditWindow')) {
|
||||
this.textEditDialogVisible = true;
|
||||
this.displayEditDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -146,6 +146,10 @@ Vue.component('font-awesome-icon', FontAwesomeIcon);
|
|||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
import "prismjs";
|
||||
import "prismjs/themes/prism.css";
|
||||
import "vue-prism-editor/dist/VuePrismEditor.css";
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
|
|
|
@ -27,11 +27,13 @@ export class Function implements INodeType {
|
|||
name: 'functionCode',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
editor: 'code',
|
||||
rows: 10,
|
||||
},
|
||||
type: 'string',
|
||||
default: 'items[0].json.myVariable = 1;\nreturn items;',
|
||||
description: 'The JavaScript code to execute.',
|
||||
noDataExpression: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -29,11 +29,13 @@ export class FunctionItem implements INodeType {
|
|||
name: 'functionCode',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
editor: 'code',
|
||||
rows: 10,
|
||||
},
|
||||
type: 'string',
|
||||
default: 'item.myVariable = 1;\nreturn item;',
|
||||
description: 'The JavaScript code to execute for each item.',
|
||||
noDataExpression: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -296,8 +296,11 @@ export interface INodeParameters {
|
|||
|
||||
export type NodePropertyTypes = 'boolean' | 'collection' | 'color' | 'dateTime' | 'fixedCollection' | 'json' | 'multiOptions' | 'number' | 'options' | 'string';
|
||||
|
||||
export type EditorTypes = 'code';
|
||||
|
||||
export interface INodePropertyTypeOptions {
|
||||
alwaysOpenEditWindow?: boolean; // Supported by: string
|
||||
editor?: EditorTypes; // Supported by: string
|
||||
loadOptionsMethod?: string; // Supported by: options
|
||||
maxValue?: number; // Supported by: number
|
||||
minValue?: number; // Supported by: number
|
||||
|
@ -307,7 +310,7 @@ export interface INodePropertyTypeOptions {
|
|||
numberStepSize?: number; // Supported by: number
|
||||
password?: boolean; // Supported by: string
|
||||
rows?: number; // Supported by: string
|
||||
[key: string]: boolean | number | string | undefined;
|
||||
[key: string]: boolean | number | string | EditorTypes | undefined;
|
||||
}
|
||||
|
||||
export interface IDisplayOptions {
|
||||
|
|
Loading…
Reference in a new issue