n8n/packages/editor-ui/src/components/RunDataJsonActions.vue
Milorad FIlipović 40e413d958
refactor(editor): Migrate part of the vuex store to pinia (#4484)
*  Added pinia support. Migrated community nodes module.
*  Added ui pinia store, moved some data from root store to it, updated modals to work with pinia stores
*  Added ui pinia store and migrated a part of the root store
*  Migrated `settings` store to pinia
*  Removing vuex store refs from router
*  Migrated `users` module to pinia store
*  Fixing errors after sync with master
*  One more error after merge
*  Created `workflows` pinia store. Moved large part of root store to it. Started updating references.
*  Finished migrating workflows store to pinia
*  Renaming some getters and actions to make more sense
*  Finished migrating the root store to pinia
*  Migrated ndv store to pinia
*  Renaming main panel dimensions getter so it doesn't clash with data prop name
* ✔️ Fixing lint errors
*  Migrated `templates` store to pinia
*  Migrated the `nodeTypes`store
*  Removed unused pieces of code and oold vuex modules
*  Adding vuex calls to pinia store, fi	xing wrong references
* 💄 Removing leftover $store refs
*  Added legacy getters and mutations to store to support webhooks
*  Added missing front-end hooks, updated vuex state subscriptions to pinia
* ✔️ Fixing linting errors
*  Removing vue composition api plugin
*  Fixing main sidebar state when loading node view
* 🐛 Fixing an error when activating workflows
* 🐛 Fixing isses with workflow settings and executions auto-refresh
* 🐛 Removing duplicate listeners which cause import error
* 🐛 Fixing route authentication
*  Updating freshly pulled $store refs
* Adding deleted const
*  Updating store references in ee features. Reseting NodeView credentials update flag when resetting workspace
*  Adding return type to email submission modal
*  Making NodeView only react to paste event when active
* 🐛 Fixing signup view errors
* 👌 Addressing PR review comments
* 👌 Addressing new PR comments
* 👌 Updating invite id logic in signup view
2022-11-04 14:04:31 +01:00

223 lines
5.6 KiB
Vue

<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";
import { mapStores } from "pinia";
import { useWorkflowsStore } from "@/stores/workflows";
import { useNDVStore } from "@/stores/ndv";
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: {
...mapStores(
useNDVStore,
useWorkflowsStore,
),
activeNode(): INodeUi | null {
return this.ndvStore.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.workflowsStore.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>