2019-06-23 03:35:23 -07:00
|
|
|
<template>
|
|
|
|
<div v-if="dialogVisible" @keydown.stop>
|
2022-12-14 01:04:10 -08:00
|
|
|
<el-dialog
|
|
|
|
:visible="dialogVisible"
|
|
|
|
custom-class="expression-dialog classic"
|
|
|
|
append-to-body
|
|
|
|
width="80%"
|
|
|
|
:title="$locale.baseText('expressionEdit.editExpression')"
|
|
|
|
:before-close="closeDialog"
|
|
|
|
>
|
2019-06-23 03:35:23 -07:00
|
|
|
<el-row>
|
|
|
|
<el-col :span="8">
|
|
|
|
<div class="header-side-menu">
|
|
|
|
<div class="headline">
|
2021-12-15 04:16:53 -08:00
|
|
|
{{ $locale.baseText('expressionEdit.editExpression') }}
|
2019-06-23 03:35:23 -07:00
|
|
|
</div>
|
|
|
|
<div class="sub-headline">
|
2021-12-15 04:16:53 -08:00
|
|
|
{{ $locale.baseText('expressionEdit.variableSelector') }}
|
2019-06-23 03:35:23 -07:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="variable-selector">
|
|
|
|
<variable-selector :path="path" @itemSelected="itemSelected"></variable-selector>
|
|
|
|
</div>
|
|
|
|
</el-col>
|
|
|
|
<el-col :span="16" class="right-side">
|
|
|
|
<div class="expression-editor-wrapper">
|
|
|
|
<div class="editor-description">
|
2022-12-01 04:26:22 -08:00
|
|
|
<div>
|
|
|
|
{{ $locale.baseText('expressionEdit.expression') }}
|
|
|
|
</div>
|
|
|
|
<div class="hint">
|
|
|
|
<span>
|
|
|
|
{{ $locale.baseText('expressionEdit.anythingInside') }}
|
|
|
|
</span>
|
|
|
|
<div class="expression-syntax-example" v-text="`{{ }}`"></div>
|
|
|
|
<span>
|
|
|
|
{{ $locale.baseText('expressionEdit.isJavaScript') }}
|
|
|
|
</span>
|
|
|
|
<n8n-link size="medium" :to="expressionsDocsUrl">
|
|
|
|
{{ $locale.baseText('expressionEdit.learnMore') }}
|
|
|
|
</n8n-link>
|
|
|
|
</div>
|
2019-06-23 03:35:23 -07:00
|
|
|
</div>
|
2022-09-21 01:20:29 -07:00
|
|
|
<div class="expression-editor ph-no-capture">
|
2022-12-14 05:43:02 -08:00
|
|
|
<ExpressionEditorModalInput
|
2022-12-01 04:26:22 -08:00
|
|
|
:value="value"
|
|
|
|
:isReadOnly="isReadOnly"
|
2023-02-02 03:35:38 -08:00
|
|
|
:path="path"
|
2022-12-01 04:26:22 -08:00
|
|
|
@change="valueChanged"
|
2022-12-20 05:38:41 -08:00
|
|
|
@close="closeDialog"
|
2022-12-01 04:26:22 -08:00
|
|
|
ref="inputFieldExpression"
|
|
|
|
data-test-id="expression-modal-input"
|
|
|
|
/>
|
2019-06-23 03:35:23 -07:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="expression-result-wrapper">
|
|
|
|
<div class="editor-description">
|
2022-12-01 04:26:22 -08:00
|
|
|
{{ $locale.baseText('expressionEdit.resultOfItem1') }}
|
2019-06-23 03:35:23 -07:00
|
|
|
</div>
|
2022-09-21 01:20:29 -07:00
|
|
|
<div class="ph-no-capture">
|
2022-12-14 05:43:02 -08:00
|
|
|
<ExpressionEditorModalOutput
|
2022-12-01 04:26:22 -08:00
|
|
|
:segments="segments"
|
|
|
|
ref="expressionResult"
|
|
|
|
data-test-id="expression-modal-output"
|
|
|
|
/>
|
2022-08-19 06:35:39 -07:00
|
|
|
</div>
|
2019-06-23 03:35:23 -07:00
|
|
|
</div>
|
|
|
|
</el-col>
|
|
|
|
</el-row>
|
|
|
|
</el-dialog>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script lang="ts">
|
2022-12-14 05:43:02 -08:00
|
|
|
import ExpressionEditorModalInput from '@/components/ExpressionEditorModal/ExpressionEditorModalInput.vue';
|
|
|
|
import ExpressionEditorModalOutput from '@/components/ExpressionEditorModal/ExpressionEditorModalOutput.vue';
|
2019-06-23 03:35:23 -07:00
|
|
|
import VariableSelector from '@/components/VariableSelector.vue';
|
|
|
|
|
2023-04-24 03:18:24 -07:00
|
|
|
import type { IVariableItemSelected } from '@/Interface';
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2022-11-23 04:41:53 -08:00
|
|
|
import { externalHooks } from '@/mixins/externalHooks';
|
|
|
|
import { genericHelpers } from '@/mixins/genericHelpers';
|
2021-05-11 20:12:53 -07:00
|
|
|
|
2022-12-01 04:26:22 -08:00
|
|
|
import { EXPRESSIONS_DOCS_URL } from '@/constants';
|
|
|
|
|
2021-05-11 20:12:53 -07:00
|
|
|
import mixins from 'vue-typed-mixins';
|
2022-11-23 04:41:53 -08:00
|
|
|
import { debounceHelper } from '@/mixins/debounce';
|
2022-11-04 06:04:31 -07:00
|
|
|
import { mapStores } from 'pinia';
|
2023-05-05 01:41:54 -07:00
|
|
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
|
|
|
import { useNDVStore } from '@/stores/ndv.store';
|
2022-12-14 05:43:02 -08:00
|
|
|
import { createExpressionTelemetryPayload } from '@/utils/telemetryUtils';
|
2022-05-23 08:56:15 -07:00
|
|
|
|
2022-12-14 05:43:02 -08:00
|
|
|
import type { Segment } from '@/types/expressions';
|
2022-12-01 04:26:22 -08:00
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
export default mixins(externalHooks, genericHelpers, debounceHelper).extend({
|
2019-06-23 03:35:23 -07:00
|
|
|
name: 'ExpressionEdit',
|
2022-12-14 01:04:10 -08:00
|
|
|
props: ['dialogVisible', 'parameter', 'path', 'value', 'eventSource'],
|
2019-06-23 03:35:23 -07:00
|
|
|
components: {
|
2022-12-14 05:43:02 -08:00
|
|
|
ExpressionEditorModalInput,
|
|
|
|
ExpressionEditorModalOutput,
|
2019-06-23 03:35:23 -07:00
|
|
|
VariableSelector,
|
|
|
|
},
|
2022-12-14 01:04:10 -08:00
|
|
|
data() {
|
2019-06-23 03:35:23 -07:00
|
|
|
return {
|
2021-05-29 19:08:41 -07:00
|
|
|
displayValue: '',
|
|
|
|
latestValue: '',
|
2022-12-01 04:26:22 -08:00
|
|
|
segments: [] as Segment[],
|
|
|
|
expressionsDocsUrl: EXPRESSIONS_DOCS_URL,
|
2019-06-23 03:35:23 -07:00
|
|
|
};
|
|
|
|
},
|
2022-11-04 06:04:31 -07:00
|
|
|
computed: {
|
2022-12-14 01:04:10 -08:00
|
|
|
...mapStores(useNDVStore, useWorkflowsStore),
|
2022-11-04 06:04:31 -07:00
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
methods: {
|
2022-12-14 01:04:10 -08:00
|
|
|
valueChanged({ value, segments }: { value: string; segments: Segment[] }, forceUpdate = false) {
|
2021-05-29 19:08:41 -07:00
|
|
|
this.latestValue = value;
|
2022-12-01 04:26:22 -08:00
|
|
|
this.segments = segments;
|
2021-05-31 10:59:45 -07:00
|
|
|
|
|
|
|
if (forceUpdate === true) {
|
|
|
|
this.updateDisplayValue();
|
|
|
|
this.$emit('valueChanged', this.latestValue);
|
|
|
|
} else {
|
2023-05-10 08:10:03 -07:00
|
|
|
void this.callDebounced('updateDisplayValue', { debounceTime: 500 });
|
2021-05-31 10:59:45 -07:00
|
|
|
}
|
2021-05-29 19:08:41 -07:00
|
|
|
},
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
updateDisplayValue() {
|
2021-05-29 19:08:41 -07:00
|
|
|
this.displayValue = this.latestValue;
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
closeDialog() {
|
2022-07-20 04:32:51 -07:00
|
|
|
if (this.latestValue !== this.value) {
|
|
|
|
// Handle the close externally as the visible parameter is an external prop
|
|
|
|
// and is so not allowed to be changed here.
|
|
|
|
this.$emit('valueChanged', this.latestValue);
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
this.$emit('closeDialog');
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
itemSelected(eventData: IVariableItemSelected) {
|
2022-12-15 05:06:00 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
(this.$refs.inputFieldExpression as any).itemSelected(eventData);
|
2022-12-14 01:04:10 -08:00
|
|
|
this.$externalHooks().run('expressionEdit.itemSelected', {
|
|
|
|
parameter: this.parameter,
|
|
|
|
value: this.value,
|
|
|
|
selectedItem: eventData,
|
|
|
|
});
|
2022-07-09 23:53:04 -07:00
|
|
|
|
|
|
|
const trackProperties: {
|
|
|
|
event_version: string;
|
|
|
|
node_type_dest: string;
|
|
|
|
node_type_source?: string;
|
|
|
|
parameter_name_dest: string;
|
|
|
|
parameter_name_source?: string;
|
|
|
|
variable_type?: string;
|
|
|
|
is_immediate_input: boolean;
|
|
|
|
variable_expression: string;
|
|
|
|
node_name: string;
|
|
|
|
} = {
|
|
|
|
event_version: '2',
|
2022-12-14 01:04:10 -08:00
|
|
|
node_type_dest: this.ndvStore.activeNode ? this.ndvStore.activeNode.type : '',
|
2022-07-09 23:53:04 -07:00
|
|
|
parameter_name_dest: this.parameter.displayName,
|
|
|
|
is_immediate_input: false,
|
|
|
|
variable_expression: eventData.variable,
|
2022-12-14 01:04:10 -08:00
|
|
|
node_name: this.ndvStore.activeNode ? this.ndvStore.activeNode.name : '',
|
2022-07-09 23:53:04 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
if (eventData.variable) {
|
|
|
|
let splitVar = eventData.variable.split('.');
|
|
|
|
|
|
|
|
if (eventData.variable.startsWith('Object.keys')) {
|
|
|
|
splitVar = eventData.variable.split('(')[1].split(')')[0].split('.');
|
|
|
|
trackProperties.variable_type = 'Keys';
|
|
|
|
} else if (eventData.variable.startsWith('Object.values')) {
|
|
|
|
splitVar = eventData.variable.split('(')[1].split(')')[0].split('.');
|
|
|
|
trackProperties.variable_type = 'Values';
|
|
|
|
} else {
|
|
|
|
trackProperties.variable_type = 'Raw value';
|
|
|
|
}
|
|
|
|
|
2023-05-12 02:45:10 -07:00
|
|
|
if (splitVar[0].startsWith("$('")) {
|
|
|
|
const match = /\$\('(.*?)'\)/.exec(splitVar[0]);
|
|
|
|
if (match && match.length > 1) {
|
|
|
|
const sourceNodeName = match[1];
|
|
|
|
trackProperties.node_type_source =
|
|
|
|
this.workflowsStore.getNodeByName(sourceNodeName)?.type;
|
|
|
|
const nodeConnections: Array<Array<{ node: string }>> =
|
|
|
|
this.workflowsStore.outgoingConnectionsByNodeName(sourceNodeName).main;
|
|
|
|
trackProperties.is_immediate_input =
|
|
|
|
nodeConnections &&
|
|
|
|
nodeConnections[0] &&
|
|
|
|
nodeConnections[0].some(({ node }) => node === this.ndvStore.activeNode?.name || '');
|
|
|
|
|
|
|
|
if (splitVar[1].startsWith('parameter')) {
|
|
|
|
trackProperties.parameter_name_source = splitVar[1].split('"')[1];
|
|
|
|
}
|
2022-07-09 23:53:04 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
trackProperties.is_immediate_input = true;
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
if (splitVar[0].startsWith('$parameter')) {
|
2022-07-09 23:53:04 -07:00
|
|
|
trackProperties.parameter_name_source = splitVar[0].split('"')[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
this.$telemetry.track(
|
|
|
|
'User inserted item from Expression Editor variable selector',
|
|
|
|
trackProperties,
|
|
|
|
);
|
2021-05-11 20:12:53 -07:00
|
|
|
},
|
|
|
|
},
|
|
|
|
watch: {
|
2022-12-14 01:04:10 -08:00
|
|
|
dialogVisible(newValue) {
|
2021-05-29 20:41:54 -07:00
|
|
|
this.displayValue = this.value;
|
|
|
|
this.latestValue = this.value;
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
const resolvedExpressionValue =
|
2022-12-15 05:06:00 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2022-12-14 01:04:10 -08:00
|
|
|
(this.$refs.expressionResult && (this.$refs.expressionResult as any).getValue()) ||
|
2022-12-15 05:06:00 -08:00
|
|
|
undefined;
|
2022-12-14 01:04:10 -08:00
|
|
|
this.$externalHooks().run('expressionEdit.dialogVisibleChanged', {
|
|
|
|
dialogVisible: newValue,
|
|
|
|
parameter: this.parameter,
|
|
|
|
value: this.value,
|
|
|
|
resolvedExpressionValue,
|
|
|
|
});
|
2021-10-18 20:57:49 -07:00
|
|
|
|
|
|
|
if (!newValue) {
|
2022-12-14 05:43:02 -08:00
|
|
|
const telemetryPayload = createExpressionTelemetryPayload(
|
|
|
|
this.segments,
|
|
|
|
this.value,
|
|
|
|
this.workflowsStore.workflowId,
|
|
|
|
this.ndvStore.sessionId,
|
|
|
|
this.ndvStore.activeNode?.type ?? '',
|
|
|
|
);
|
2022-12-01 04:26:22 -08:00
|
|
|
|
2022-08-19 06:35:39 -07:00
|
|
|
this.$telemetry.track('User closed Expression Editor', telemetryPayload);
|
2023-05-10 08:10:03 -07:00
|
|
|
void this.$externalHooks().run('expressionEdit.closeDialog', telemetryPayload);
|
2021-10-18 20:57:49 -07:00
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
.editor-description {
|
2021-08-29 04:36:17 -07:00
|
|
|
line-height: 1.5;
|
2019-06-23 03:35:23 -07:00
|
|
|
font-weight: bold;
|
2022-12-01 04:26:22 -08:00
|
|
|
padding: 0 0 0.5em 0.2em;
|
|
|
|
display: flex;
|
2022-12-14 01:04:10 -08:00
|
|
|
justify-content: space-between;
|
2022-12-01 04:26:22 -08:00
|
|
|
|
|
|
|
.hint {
|
|
|
|
color: var(--color-text-base);
|
|
|
|
font-weight: normal;
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
@media (max-width: $breakpoint-xs) {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
span {
|
|
|
|
margin-right: var(--spacing-4xs);
|
|
|
|
}
|
|
|
|
.expression-syntax-example {
|
|
|
|
display: inline-block;
|
|
|
|
margin-top: 3px;
|
|
|
|
height: 16px;
|
|
|
|
line-height: 1;
|
|
|
|
background-color: var(--color-expression-syntax-example);
|
|
|
|
color: var(--color-text-dark);
|
|
|
|
margin-right: var(--spacing-4xs);
|
|
|
|
}
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
.expression-result-wrapper,
|
|
|
|
.expression-editor-wrapper {
|
|
|
|
padding: 10px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.expression-result-wrapper {
|
|
|
|
margin-top: 1em;
|
|
|
|
}
|
|
|
|
|
2020-02-07 08:36:23 -08:00
|
|
|
::v-deep .expression-dialog {
|
2019-06-23 03:35:23 -07:00
|
|
|
.el-dialog__header {
|
|
|
|
padding: 0;
|
|
|
|
}
|
|
|
|
.el-dialog__title {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
.el-dialog__body {
|
|
|
|
padding: 0;
|
2021-10-27 12:55:37 -07:00
|
|
|
font-size: var(--font-size-s);
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
.right-side {
|
2022-07-26 03:45:55 -07:00
|
|
|
background-color: var(--color-background-light);
|
2021-09-11 01:15:36 -07:00
|
|
|
border-top-right-radius: 8px;
|
|
|
|
border-bottom-right-radius: 8px;
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.header-side-menu {
|
|
|
|
padding: 1em 0 0.5em 1.8em;
|
2021-09-11 01:15:36 -07:00
|
|
|
border-top-left-radius: 8px;
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
background-color: var(--color-background-base);
|
2022-07-26 03:45:55 -07:00
|
|
|
color: var(--color-text-dark);
|
2022-09-23 07:14:28 -07:00
|
|
|
border-bottom: 1px solid $color-primary;
|
2019-06-23 03:35:23 -07:00
|
|
|
margin-bottom: 1em;
|
|
|
|
|
|
|
|
.headline {
|
|
|
|
font-size: 1.35em;
|
|
|
|
font-weight: 600;
|
2021-08-29 04:36:17 -07:00
|
|
|
line-height: 1.5;
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
.sub-headline {
|
|
|
|
font-weight: 600;
|
|
|
|
font-size: 1.1em;
|
|
|
|
text-align: center;
|
2021-08-29 04:36:17 -07:00
|
|
|
line-height: 1.5;
|
2019-06-23 03:35:23 -07:00
|
|
|
padding-top: 1.5em;
|
2022-09-23 07:14:28 -07:00
|
|
|
color: $color-primary;
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.variable-selector {
|
|
|
|
margin: 0 1em;
|
|
|
|
}
|
|
|
|
</style>
|