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",
|
"lodash.set": "^4.3.2",
|
||||||
"n8n-workflow": "^0.10.0",
|
"n8n-workflow": "^0.10.0",
|
||||||
"node-sass": "^4.12.0",
|
"node-sass": "^4.12.0",
|
||||||
|
"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-loader": "^7.0.1",
|
"sass-loader": "^7.0.1",
|
||||||
|
@ -72,6 +73,7 @@
|
||||||
"vue": "^2.6.9",
|
"vue": "^2.6.9",
|
||||||
"vue-cli-plugin-webpack-bundle-analyzer": "^1.3.0",
|
"vue-cli-plugin-webpack-bundle-analyzer": "^1.3.0",
|
||||||
"vue-json-pretty": "^1.4.1",
|
"vue-json-pretty": "^1.4.1",
|
||||||
|
"vue-prism-editor": "^0.3.0",
|
||||||
"vue-router": "^3.0.6",
|
"vue-router": "^3.0.6",
|
||||||
"vue-template-compiler": "^2.5.17",
|
"vue-template-compiler": "^2.5.17",
|
||||||
"vue-typed-mixins": "^0.1.0",
|
"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>
|
<template>
|
||||||
<div @keydown.stop :class="parameterInputClasses">
|
<div @keydown.stop :class="parameterInputClasses">
|
||||||
<expression-edit :dialogVisible="expressionEditDialogVisible" :value="value" :parameter="parameter" :path="path" @closeDialog="closeExpressionEditDialog" @valueChanged="expressionUpdated"></expression-edit>
|
<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">
|
<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">
|
<div v-if="['json', 'string'].includes(parameter.type)">
|
||||||
<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" />
|
<code-edit :dialogVisible="codeEditDialogVisible" :value="value" :parameter="parameter" @closeDialog="closeCodeEditDialog" @valueChanged="expressionUpdated"></code-edit>
|
||||||
</el-input>
|
<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'">
|
<div v-else-if="parameter.type === 'dateTime'">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="tempValue"
|
v-model="tempValue"
|
||||||
|
@ -109,7 +116,10 @@ import {
|
||||||
Workflow,
|
Workflow,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import CodeEdit from '@/components/CodeEdit.vue';
|
||||||
import ExpressionEdit from '@/components/ExpressionEdit.vue';
|
import ExpressionEdit from '@/components/ExpressionEdit.vue';
|
||||||
|
// @ts-ignore
|
||||||
|
import PrismEditor from 'vue-prism-editor';
|
||||||
import TextEdit from '@/components/TextEdit.vue';
|
import TextEdit from '@/components/TextEdit.vue';
|
||||||
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
||||||
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
||||||
|
@ -127,7 +137,9 @@ export default mixins(
|
||||||
.extend({
|
.extend({
|
||||||
name: 'ParameterInput',
|
name: 'ParameterInput',
|
||||||
components: {
|
components: {
|
||||||
|
CodeEdit,
|
||||||
ExpressionEdit,
|
ExpressionEdit,
|
||||||
|
PrismEditor,
|
||||||
TextEdit,
|
TextEdit,
|
||||||
},
|
},
|
||||||
props: [
|
props: [
|
||||||
|
@ -139,6 +151,7 @@ export default mixins(
|
||||||
],
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
codeEditDialogVisible: false,
|
||||||
nodeName: '',
|
nodeName: '',
|
||||||
expressionAddOperation: 'set' as 'add' | 'set',
|
expressionAddOperation: 'set' as 'add' | 'set',
|
||||||
expressionEditDialogVisible: false,
|
expressionEditDialogVisible: false,
|
||||||
|
@ -324,6 +337,9 @@ export default mixins(
|
||||||
isDefault (): boolean {
|
isDefault (): boolean {
|
||||||
return this.parameter.default === this.value;
|
return this.parameter.default === this.value;
|
||||||
},
|
},
|
||||||
|
isEditor (): boolean {
|
||||||
|
return this.getArgument('editor') === 'code';
|
||||||
|
},
|
||||||
isValueExpression () {
|
isValueExpression () {
|
||||||
if (this.parameter.noDataExpression === true) {
|
if (this.parameter.noDataExpression === true) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -401,12 +417,24 @@ export default mixins(
|
||||||
|
|
||||||
this.remoteParameterOptionsLoading = false;
|
this.remoteParameterOptionsLoading = false;
|
||||||
},
|
},
|
||||||
|
closeCodeEditDialog () {
|
||||||
|
this.codeEditDialogVisible = false;
|
||||||
|
},
|
||||||
closeExpressionEditDialog () {
|
closeExpressionEditDialog () {
|
||||||
this.expressionEditDialogVisible = false;
|
this.expressionEditDialogVisible = false;
|
||||||
},
|
},
|
||||||
closeTextEditDialog () {
|
closeTextEditDialog () {
|
||||||
this.textEditDialogVisible = false;
|
this.textEditDialogVisible = false;
|
||||||
},
|
},
|
||||||
|
displayEditDialog () {
|
||||||
|
console.log('displayEditDialog...');
|
||||||
|
|
||||||
|
if (this.isEditor) {
|
||||||
|
this.codeEditDialogVisible = true;
|
||||||
|
} else {
|
||||||
|
this.textEditDialogVisible = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
getArgument (argumentName: string): string | number | boolean | undefined {
|
getArgument (argumentName: string): string | number | boolean | undefined {
|
||||||
if (this.parameter.typeOptions === undefined) {
|
if (this.parameter.typeOptions === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -431,7 +459,7 @@ export default mixins(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.parameter.type === 'string' && this.getArgument('alwaysOpenEditWindow')) {
|
if (this.parameter.type === 'string' && this.getArgument('alwaysOpenEditWindow')) {
|
||||||
this.textEditDialogVisible = true;
|
this.displayEditDialog();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,6 +146,10 @@ Vue.component('font-awesome-icon', FontAwesomeIcon);
|
||||||
|
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
|
|
||||||
|
import "prismjs";
|
||||||
|
import "prismjs/themes/prism.css";
|
||||||
|
import "vue-prism-editor/dist/VuePrismEditor.css";
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
router,
|
router,
|
||||||
store,
|
store,
|
||||||
|
|
|
@ -27,11 +27,13 @@ export class Function implements INodeType {
|
||||||
name: 'functionCode',
|
name: 'functionCode',
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
alwaysOpenEditWindow: true,
|
alwaysOpenEditWindow: true,
|
||||||
|
editor: 'code',
|
||||||
rows: 10,
|
rows: 10,
|
||||||
},
|
},
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: 'items[0].json.myVariable = 1;\nreturn items;',
|
default: 'items[0].json.myVariable = 1;\nreturn items;',
|
||||||
description: 'The JavaScript code to execute.',
|
description: 'The JavaScript code to execute.',
|
||||||
|
noDataExpression: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -29,11 +29,13 @@ export class FunctionItem implements INodeType {
|
||||||
name: 'functionCode',
|
name: 'functionCode',
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
alwaysOpenEditWindow: true,
|
alwaysOpenEditWindow: true,
|
||||||
|
editor: 'code',
|
||||||
rows: 10,
|
rows: 10,
|
||||||
},
|
},
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: 'item.myVariable = 1;\nreturn item;',
|
default: 'item.myVariable = 1;\nreturn item;',
|
||||||
description: 'The JavaScript code to execute for each 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 NodePropertyTypes = 'boolean' | 'collection' | 'color' | 'dateTime' | 'fixedCollection' | 'json' | 'multiOptions' | 'number' | 'options' | 'string';
|
||||||
|
|
||||||
|
export type EditorTypes = 'code';
|
||||||
|
|
||||||
export interface INodePropertyTypeOptions {
|
export interface INodePropertyTypeOptions {
|
||||||
alwaysOpenEditWindow?: boolean; // Supported by: string
|
alwaysOpenEditWindow?: boolean; // Supported by: string
|
||||||
|
editor?: EditorTypes; // Supported by: string
|
||||||
loadOptionsMethod?: string; // Supported by: options
|
loadOptionsMethod?: string; // Supported by: options
|
||||||
maxValue?: number; // Supported by: number
|
maxValue?: number; // Supported by: number
|
||||||
minValue?: number; // Supported by: number
|
minValue?: number; // Supported by: number
|
||||||
|
@ -307,7 +310,7 @@ export interface INodePropertyTypeOptions {
|
||||||
numberStepSize?: number; // Supported by: number
|
numberStepSize?: number; // Supported by: number
|
||||||
password?: boolean; // Supported by: string
|
password?: boolean; // Supported by: string
|
||||||
rows?: number; // Supported by: string
|
rows?: number; // Supported by: string
|
||||||
[key: string]: boolean | number | string | undefined;
|
[key: string]: boolean | number | string | EditorTypes | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDisplayOptions {
|
export interface IDisplayOptions {
|
||||||
|
|
Loading…
Reference in a new issue