feat(editor): update expressions display (#4171)

* N8n 4673 expressions res1 (#4149)

* hide hints if necessary

* refactor out parameter input

* refactor param input in creds

* remove any

* add expression result before

* update case

* add types

* fix spacing

* update types

* update expr

* update parameter input

* update param input

* update param input

* remove import

* fix typo

* update value

* fix drop for rl

* add state to track hovering item

* add hover behavior to resolve values

* update index

* fix run selector bug

* add run item to eval expr

* add paired item mappings

* fix rec bug

* Fix for loops

* handle pinned data

* add missing pinned

* fix bug

* support parent

* add input

* map back from output

* clean up

* fix output bug

* fix branching bug

* update preview

* only if expr

* fix output

* fix expr eval for outputs

* add default hover state

* fix hover state

* fix branching

* hide hint if expr

* remove duplicate logic

* update style

* allow opening expr in demo

* update expr

* update row hover

* update param name

* clean up

* update hovering state

* update default output

* fix duplicate import

* update hover behavior

* update package lock

* fix pinned data case

* address case when no input
This commit is contained in:
Mutasem Aldmour
2022-10-12 14:06:28 +02:00
committed by GitHub
parent fe7c8a85ce
commit 6b538494ce
28 changed files with 842 additions and 228 deletions

View File

@@ -21,6 +21,7 @@
:value="value"
:displayTitle="displayTitle"
:expressionDisplayValue="expressionDisplayValue"
:expressionComputedValue="expressionEvaluated"
:isValueExpression="isValueExpression"
:isReadOnly="isReadOnly"
:parameterIssues="getIssues"
@@ -37,7 +38,7 @@
:size="inputSize"
:type="getStringInputType"
:rows="getArgument('rows')"
:value="activeDrop || forceShowExpression? '': expressionDisplayValue"
:value="expressionDisplayValue"
:title="displayTitle"
@keydown.stop
/>
@@ -297,6 +298,8 @@ import {
INodeParameters,
INodePropertyOptions,
Workflow,
INodeProperties,
INodePropertyCollection,
NodeParameterValueType,
} from 'n8n-workflow';
@@ -316,12 +319,13 @@ import { externalHooks } from '@/components/mixins/externalHooks';
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
import { showMessage } from '@/components/mixins/showMessage';
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
import { hasExpressionMapping, isValueExpression } from './helpers';
import { isResourceLocatorValue } from '@/typeGuards';
import mixins from 'vue-typed-mixins';
import { CUSTOM_API_CALL_KEY } from '@/constants';
import { mapGetters } from 'vuex';
import { hasExpressionMapping, isValueExpression } from './helpers';
import { isResourceLocatorValue } from '@/typeGuards';
import { PropType } from 'vue';
export default mixins(
externalHooks,
@@ -344,21 +348,56 @@ export default mixins(
TextEdit,
ImportParameter,
},
props: [
'inputSize',
'isReadOnly',
'documentationUrl',
'parameter', // NodeProperties
'path', // string
'value',
'hideIssues', // boolean
'errorHighlight',
'isForCredential', // boolean
'eventSource', // string
'activeDrop',
'droppable',
'forceShowExpression',
],
props: {
isReadOnly: {
type: Boolean,
},
parameter: {
type: Object as PropType<INodeProperties>,
},
path: {
type: String,
},
value: {
type: [String, Number, Boolean, Array, Object] as PropType<NodeParameterValueType>,
},
hideLabel: {
type: Boolean,
},
droppable: {
type: Boolean,
},
activeDrop: {
type: Boolean,
},
forceShowExpression: {
type: Boolean,
},
hint: {
type: String as PropType<string | undefined>,
},
inputSize: {
type: String,
},
hideIssues: {
type: Boolean,
},
documentationUrl: {
type: String as PropType<string | undefined>,
},
errorHighlight: {
type: Boolean,
},
isForCredential: {
type: Boolean,
},
eventSource: {
type: String,
},
expressionEvaluated: {
type: String as PropType<string | undefined>,
},
},
data () {
return {
codeEditDialogVisible: false,
@@ -419,12 +458,21 @@ export default mixins(
},
computed: {
...mapGetters('credentials', ['allCredentialTypes']),
expressionDisplayValue(): string {
if (this.activeDrop || this.forceShowExpression) {
return '';
}
const value = isResourceLocatorValue(this.value) ? this.value.value : this.value;
if (typeof value === 'string' && value.startsWith('=')) {
return value.slice(1);
}
return '';
},
isValueExpression(): boolean {
return isValueExpression(this.parameter, this.value);
},
areExpressionsDisabled(): boolean {
return this.$store.getters['ui/areExpressionsDisabled'];
},
codeAutocomplete (): string | undefined {
return this.getArgument('codeAutocomplete') as string | undefined;
},
@@ -486,9 +534,9 @@ export default mixins(
let returnValue;
if (this.isValueExpression === false) {
returnValue = this.isResourceLocatorParameter ? (this.value ? this.value.value: '') : this.value;
returnValue = this.isResourceLocatorParameter ? (isResourceLocatorValue(this.value) ? this.value.value: '') : this.value;
} else {
returnValue = this.expressionValueComputed;
returnValue = this.expressionEvaluated;
}
if (this.parameter.type === 'credentialsSelect') {
@@ -519,39 +567,6 @@ export default mixins(
return returnValue;
},
expressionDisplayValue (): string {
const value = this.displayValue;
// address type errors for text input
if (typeof value === 'number' || typeof value === 'boolean') {
return JSON.stringify(value);
}
if (value === null) {
return '';
}
return value;
},
expressionValueComputed (): NodeParameterValue | string[] | null {
if (this.areExpressionsDisabled) {
return this.value;
}
if (this.node === null) {
return null;
}
let computedValue: NodeParameterValue;
try {
computedValue = this.resolveExpression(this.value.value || this.value) as NodeParameterValue;
} catch (error) {
computedValue = `[${this.$locale.baseText('parameterInput.error')}}: ${error.message}]`;
}
return computedValue;
},
getStringInputType () {
if (this.getArgument('password') === true) {
return 'password';
@@ -562,7 +577,7 @@ export default mixins(
return 'textarea';
}
if (this.parameter.type === 'code') {
if (this.parameter.typeOptions && this.parameter.typeOptions.editor === 'code') {
return 'textarea';
}
@@ -587,13 +602,14 @@ export default mixins(
} else if (
['options', 'multiOptions'].includes(this.parameter.type) &&
this.remoteParameterOptionsLoading === false &&
this.remoteParameterOptionsLoadingIssues === null
this.remoteParameterOptionsLoadingIssues === null &&
this.parameterOptions
) {
// Check if the value resolves to a valid option
// Currently it only displays an error in the node itself in
// case the value is not valid. The workflow can still be executed
// and the error is not displayed on the node in the workflow
const validOptions = this.parameterOptions!.map((options: INodePropertyOptions) => options.value);
const validOptions = this.parameterOptions.map((options) => (options as INodePropertyOptions).value);
const checkValues: string[] = [];
@@ -640,7 +656,7 @@ export default mixins(
editorType (): string {
return this.getArgument('editor') as string;
},
parameterOptions (): INodePropertyOptions[] {
parameterOptions (): Array<INodePropertyOptions | INodeProperties | INodePropertyCollection> | undefined {
if (this.hasRemoteMethod === false) {
// Options are already given
return this.parameter.options;
@@ -828,10 +844,6 @@ export default mixins(
this.valueChanged(val);
},
openExpressionEdit() {
if (this.areExpressionsDisabled) {
return;
}
if (this.isValueExpression) {
this.expressionEditDialogVisible = true;
this.trackExpressionEditOpen();
@@ -896,7 +908,7 @@ export default mixins(
this.$emit('textInput', parameterData);
},
valueChanged (value: string[] | string | number | boolean | Date | {} | null) {
valueChanged (value: NodeParameterValueType | {} | Date) {
if (this.parameter.name === 'nodeCredentialType') {
this.activeCredentialType = value as string;
}
@@ -905,7 +917,7 @@ export default mixins(
value = value.toISOString();
}
if (this.parameter.type === 'color' && this.getArgument('showAlpha') === true && value !== null && value.toString().charAt(0) !== '#') {
if (this.parameter.type === 'color' && this.getArgument('showAlpha') === true && value !== null && value !== undefined && value.toString().charAt(0) !== '#') {
const newValue = this.rgbaToHex(value as string);
if (newValue !== null) {
this.tempValue = newValue;
@@ -959,14 +971,14 @@ export default mixins(
this.trackExpressionEditOpen();
}, 375);
} else if (command === 'removeExpression') {
let value = this.expressionValueComputed;
let value: NodeParameterValueType = this.expressionEvaluated;
if (this.parameter.type === 'multiOptions' && typeof value === 'string') {
value = (value || '').split(',')
.filter((value) => (this.parameterOptions || []).find((option) => option.value === value));
.filter((value) => (this.parameterOptions || []).find((option) => (option as INodePropertyOptions).value === value));
}
if (this.isResourceLocatorParameter) {
if (this.isResourceLocatorParameter && isResourceLocatorValue(this.value)) {
this.valueChanged({ __rl: true, value, mode: this.value.mode });
} else {
this.valueChanged(typeof value !== 'undefined' ? value : null);