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:
Csaba Tuncsik
2022-10-06 15:03:55 +02:00
committed by GitHub
parent e63eee28e0
commit 19e333e660
21 changed files with 1122 additions and 451 deletions

View 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>