mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
feat(editor-ui): JSON mapping (#4270)
* refactor(editor-ui): update 'vue-json-pretty' and adjust component to preserve same behaviour (#4152) * fix(editor-ui): export interface to solve 'TS4082: Default export of the module has or is using private name' error temporarily * refactor(editor-ui): update 'vue-json-pretty' and adjust component to preserve same behaviour * refactor(editor-ui): move json data view into its own component (#4158) * refactor(editor-ui): move json data view into its own component * fix(editor-ui): make JSON data component work again * fix(editor-ui): JSON data component type issues * fix(editor-ui): JSON data component prop 'inputData' * refactor(editor-ui): rename helper function * fix(editor-ui): add declaration to `vue-json-pretty` component * refactor(editor-ui): JSON mapping move more logic to new component * refactor(editor-ui): some cleanup in JSON mapping component * refactor(editor-ui): changing key mapping translation * refactor(editor-ui): add basic drag'n'drop functionality to JSON view * refactor(editor-ui): moving JSON view actions into separate components * fix(editor-ui): JSON view action copy default selected path * fix(editor-ui): refactor draggable to play nicer with other (3rd party) components * fix(editor-ui): improve draggable performance * fix(editor-ui): add disable user selection class to body * fix(editor-ui): reduce click handler cognitive load in JSON view copy actions * fix(editor-ui): JSON view mapped path * fix(editor-ui): remove unnecessary wrapper around RunDataTable.vue * fix(editor-ui): respect input node distance when json parameter path is copied * fix(editor-ui): JSON mapping property highlight * fix(editor-ui): block event only on mousemove for draggable to not select content * refactor(editor-ui): fixing prop types and organising imports * fix(editor-ui): JSON view use double quotes where appropriate * fix(editor-ui): fix new package additions after merge conflict * fix(editor-ui): fix package update after merge conflict * fix(editor-ui): JSON view prop names text break * fix(editor-ui): use kebab-case name for component * fix(editor-ui): calling convertPath on draggable node path * feat(editor-ui): add mapping discoverability tooltip to mappable inputs (#4227) * refactor(editor-ui): move json data view into its own component * fix(editor-ui): make JSON data component work again * fix(editor-ui): JSON data component type issues * fix(editor-ui): JSON data component prop 'inputData' * refactor(editor-ui): rename helper function * fix(editor-ui): add declaration to `vue-json-pretty` component * refactor(editor-ui): JSON mapping move more logic to new component * refactor(editor-ui): some cleanup in JSON mapping component * refactor(editor-ui): changing key mapping translation * refactor(editor-ui): add basic drag'n'drop functionality to JSON view * refactor(editor-ui): moving JSON view actions into separate components * fix(editor-ui): JSON view action copy default selected path * fix(editor-ui): refactor draggable to play nicer with other (3rd party) components * fix(editor-ui): improve draggable performance * fix(editor-ui): add disable user selection class to body * fix(editor-ui): reduce click handler cognitive load in JSON view copy actions * fix(editor-ui): JSON view mapped path * fix(editor-ui): remove unnecessary wrapper around RunDataTable.vue * fix(editor-ui): respect input node distance when json parameter path is copied * fix(editor-ui): JSON mapping property highlight * fix(editor-ui): block event only on mousemove for draggable to not select content * refactor(editor-ui): fixing prop types and organising imports * fix(editor-ui): JSON view use double quotes where appropriate * fix(editor-ui): fix new package additions after merge conflict * fix(editor-ui): fix package update after merge conflict * fix(editor-ui): JSON view prop names text break * fix(editor-ui): update helper after merge conflict * refactor(editor-ui): cleanup RunaDataTable tooltips * refactor(editor-ui): add temporary static tooltip to input with mapping * fix(editor-ui): input mapping tooltip proper input name * fix(editor-ui): show input mapping tooltip when conditions are met * fix(editor-ui): show different input mapping tooltip for different view types (table, json) * fix(editor-ui): drop lodash isEmpty * fix(editor-ui): using and keeping only getter function * fix(editor-ui): check `INodeExecutionData[]` array emptyness (still needs some improvement) * feat(editor-ui): add telemetry calls to data mapping (#4250) * fix(editor-ui): add types package for jsonpath * fix(editor-ui): JSON view drag'n'drop telemetry call * fix(editor-ui): add data mapping tooltip close telemetry to parameter input * fix(editor-ui): execute previous node tooltip linebreak * fix(editor-ui): input data mapping tooltip show-hide logic * fix(editor-ui): input data mapping tooltip position * fix(editor-ui): using a placeholder gif in mapping discoverability tooltip * refactor(design-system): adding optional configurable buttons to tooltip (#4260) * refactor(design-system): unbreaking wrapper around element ui tooltip * fix(design-system): update test snapshot * refactor(design-system): adding buttons to tooltip * fix(design-system): update test snapshot * fix(design-system): change tooltip props and some cleanup * fix(design-system): update test snapshot * chore: fix package lock file after merge * fix(editor-ui): modifications according to Max's review (#4273) * fix(editor-ui): modifications according to Max's review * fix(editor-ui): JSON prop names should not be written bold * fix(editor-ui): use proper animated gif in JSON data mapping discoverability tooltip
This commit is contained in:
215
packages/editor-ui/src/components/RunDataJsonActions.vue
Normal file
215
packages/editor-ui/src/components/RunDataJsonActions.vue
Normal file
@@ -0,0 +1,215 @@
|
||||
<template>
|
||||
<div :class="$style.actionsGroup">
|
||||
<el-dropdown trigger="click" @command="handleCopyClick">
|
||||
<span class="el-dropdown-link">
|
||||
<n8n-icon-button
|
||||
:title="$locale.baseText('runData.copyToClipboard')"
|
||||
icon="copy"
|
||||
type="tertiary"
|
||||
:circle="false"
|
||||
/>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :command="{command: 'value'}">
|
||||
{{ $locale.baseText('runData.copyValue') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{command: 'itemPath'}" divided>
|
||||
{{ $locale.baseText('runData.copyItemPath') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{command: 'parameterPath'}">
|
||||
{{ $locale.baseText('runData.copyParameterPath') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType } from "vue";
|
||||
import mixins from "vue-typed-mixins";
|
||||
import jp from "jsonpath";
|
||||
import { INodeUi } from "@/Interface";
|
||||
import { IDataObject } from "n8n-workflow";
|
||||
import { copyPaste } from "@/components/mixins/copyPaste";
|
||||
import { pinData } from "@/components/mixins/pinData";
|
||||
import { nodeHelpers } from "@/components/mixins/nodeHelpers";
|
||||
import { genericHelpers } from "@/components/mixins/genericHelpers";
|
||||
import { clearJsonKey, convertPath, executionDataToJson } from "@/components/helpers";
|
||||
|
||||
type JsonPathData = {
|
||||
path: string;
|
||||
startPath: string;
|
||||
};
|
||||
|
||||
// A path that does not exist so that nothing is selected by default
|
||||
const nonExistingJsonPath = '_!^&*';
|
||||
|
||||
export default mixins(
|
||||
genericHelpers,
|
||||
nodeHelpers,
|
||||
pinData,
|
||||
copyPaste,
|
||||
).extend({
|
||||
name: 'run-data-json-actions',
|
||||
props: {
|
||||
node: {
|
||||
type: Object as PropType<INodeUi>,
|
||||
},
|
||||
paneType: {
|
||||
type: String,
|
||||
},
|
||||
sessionId: {
|
||||
type: String,
|
||||
},
|
||||
currentOutputIndex: {
|
||||
type: Number,
|
||||
},
|
||||
runIndex: {
|
||||
type: Number,
|
||||
},
|
||||
displayMode: {
|
||||
type: String,
|
||||
},
|
||||
distanceFromActive: {
|
||||
type: Number,
|
||||
},
|
||||
selectedJsonPath: {
|
||||
type: String,
|
||||
default: nonExistingJsonPath,
|
||||
},
|
||||
jsonData: {
|
||||
type: Array as PropType<IDataObject[]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
activeNode(): INodeUi {
|
||||
return this.$store.getters.activeNode;
|
||||
},
|
||||
normalisedJsonPath(): string {
|
||||
const isNotSelected = this.selectedJsonPath === nonExistingJsonPath;
|
||||
return isNotSelected ? '[""]' : this.selectedJsonPath;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getJsonValue(): string {
|
||||
let selectedValue = jp.query(this.jsonData, `$${ this.normalisedJsonPath }`)[0];
|
||||
if (this.selectedJsonPath === nonExistingJsonPath) {
|
||||
if (this.hasPinData) {
|
||||
selectedValue = clearJsonKey(this.pinData as object);
|
||||
} else {
|
||||
selectedValue = executionDataToJson(this.getNodeInputData(this.node, this.runIndex, this.currentOutputIndex));
|
||||
}
|
||||
}
|
||||
|
||||
let value = '';
|
||||
if (typeof selectedValue === 'object') {
|
||||
value = JSON.stringify(selectedValue, null, 2);
|
||||
} else {
|
||||
value = selectedValue.toString();
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
getJsonItemPath(): JsonPathData {
|
||||
const newPath = convertPath(this.normalisedJsonPath);
|
||||
let startPath = '';
|
||||
let path = '';
|
||||
|
||||
const pathParts = newPath.split(']');
|
||||
const index = pathParts[0].slice(1);
|
||||
path = pathParts.slice(1).join(']');
|
||||
startPath = `$item(${ index }).$node["${ this.node!.name }"].json`;
|
||||
|
||||
return { path, startPath };
|
||||
},
|
||||
getJsonParameterPath(): JsonPathData {
|
||||
const newPath = convertPath(this.normalisedJsonPath);
|
||||
const path = newPath.split(']').slice(1).join(']');
|
||||
let startPath = `$node["${ this.node!.name }"].json`;
|
||||
|
||||
if (this.distanceFromActive === 1) {
|
||||
startPath = `$json`;
|
||||
}
|
||||
|
||||
return { path, startPath };
|
||||
},
|
||||
handleCopyClick(commandData: { command: string }) {
|
||||
let value: string;
|
||||
if (commandData.command === 'value') {
|
||||
value = this.getJsonValue();
|
||||
|
||||
this.$showToast({
|
||||
title: this.$locale.baseText('runData.copyValue.toast'),
|
||||
message: '',
|
||||
type: 'success',
|
||||
duration: 2000,
|
||||
});
|
||||
} else {
|
||||
let startPath = '';
|
||||
let path = '';
|
||||
if (commandData.command === 'itemPath') {
|
||||
const jsonItemPath = this.getJsonItemPath();
|
||||
startPath = jsonItemPath.startPath;
|
||||
path = jsonItemPath.path;
|
||||
|
||||
this.$showToast({
|
||||
title: this.$locale.baseText('runData.copyItemPath.toast'),
|
||||
message: '',
|
||||
type: 'success',
|
||||
duration: 2000,
|
||||
});
|
||||
} else if (commandData.command === 'parameterPath') {
|
||||
const jsonParameterPath = this.getJsonParameterPath();
|
||||
startPath = jsonParameterPath.startPath;
|
||||
path = jsonParameterPath.path;
|
||||
|
||||
this.$showToast({
|
||||
title: this.$locale.baseText('runData.copyParameterPath.toast'),
|
||||
message: '',
|
||||
type: 'success',
|
||||
duration: 2000,
|
||||
});
|
||||
}
|
||||
if (!path.startsWith('[') && !path.startsWith('.') && path) {
|
||||
path += '.';
|
||||
}
|
||||
value = `{{ ${ startPath + path } }}`;
|
||||
}
|
||||
|
||||
const copyType = {
|
||||
value: 'selection',
|
||||
itemPath: 'item_path',
|
||||
parameterPath: 'parameter_path',
|
||||
}[commandData.command];
|
||||
|
||||
this.$telemetry.track('User copied ndv data', {
|
||||
node_type: this.activeNode.type,
|
||||
session_id: this.sessionId,
|
||||
run_index: this.runIndex,
|
||||
view: 'json',
|
||||
copy_type: copyType,
|
||||
workflow_id: this.$store.getters.workflowId,
|
||||
pane: this.paneType,
|
||||
in_execution_log: this.isReadOnly,
|
||||
});
|
||||
|
||||
this.copyToClipboard(value);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.actionsGroup {
|
||||
position: sticky;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
z-index: 10;
|
||||
top: 0;
|
||||
padding-right: var(--spacing-s);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user