mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
💄 Improve editor-ui by displaying clearer what each node does
This commit is contained in:
parent
0c89445682
commit
8482e1b363
|
@ -9,33 +9,32 @@
|
|||
<el-badge v-else :hidden="workflowDataItems === 0" class="node-info-icon data-count" :value="workflowDataItems"></el-badge>
|
||||
|
||||
<div class="node-executing-info" title="Node is executing">
|
||||
<font-awesome-icon icon="spinner" spin />
|
||||
</div>
|
||||
<div class="node-execute" v-if="!isReadOnly && !workflowRunning">
|
||||
<font-awesome-icon class="execute-icon" @click.stop.left="executeNode" icon="play-circle" title="Execute Node"/>
|
||||
<font-awesome-icon icon="sync-alt" spin />
|
||||
</div>
|
||||
<div class="node-options" v-if="!isReadOnly">
|
||||
<div @click.stop.left="deleteNode" class="option indent" title="Delete Node" >
|
||||
<div @click.stop.left="deleteNode" class="option" title="Delete Node" >
|
||||
<font-awesome-icon icon="trash" />
|
||||
</div>
|
||||
<div @click.stop.left="disableNode" class="option" title="Activate/Deactivate Node" >
|
||||
<font-awesome-icon :icon="nodeDisabledIcon" />
|
||||
</div>
|
||||
<div @click.stop.left="duplicateNode" class="option" title="Duplicate Node" >
|
||||
<font-awesome-icon icon="clone" />
|
||||
</div>
|
||||
<div @click.stop.left="disableNode" class="option indent" title="Activate/Deactivate Node" >
|
||||
<font-awesome-icon :icon="nodeDisabledIcon" />
|
||||
<div @click.stop.left="executeNode" class="option" title="Execute Node" v-if="!isReadOnly && !workflowRunning">
|
||||
<font-awesome-icon class="execute-icon" icon="play-circle" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="node-description">
|
||||
<div class="node-name" :title="data.name">
|
||||
{{data.name}}
|
||||
</div>
|
||||
<div v-if="nodeSubtitle !== undefined" class="node-subtitle" :title="nodeSubtitle">
|
||||
{{nodeSubtitle}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NodeIcon class="node-icon" :nodeType="nodeType" :style="nodeIconStyle"/>
|
||||
<div class="node-name" :title="data.name">
|
||||
{{data.name}}
|
||||
</div>
|
||||
<div v-if="nodeSubtitle !== undefined" class="node-subtitle" :title="nodeSubtitle">
|
||||
{{nodeSubtitle}}
|
||||
</div>
|
||||
<div class="node-edit" @click.left.stop="setNodeActive" title="Edit Node">
|
||||
<font-awesome-icon icon="pen" />
|
||||
</div>
|
||||
<NodeIcon class="node-icon" :nodeType="nodeType" size="60" :style="nodeIconStyle"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -90,10 +89,6 @@ export default mixins(nodeBase, workflowHelpers).extend({
|
|||
classes.push('disabled');
|
||||
}
|
||||
|
||||
if (this.nodeSubtitle) {
|
||||
classes.push('has-subtitle');
|
||||
}
|
||||
|
||||
if (this.isExecuting) {
|
||||
classes.push('executing');
|
||||
}
|
||||
|
@ -203,35 +198,20 @@ export default mixins(nodeBase, workflowHelpers).extend({
|
|||
|
||||
.node-default {
|
||||
position: absolute;
|
||||
width: 160px;
|
||||
height: 50px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: #fff;
|
||||
border-radius: 25px;
|
||||
text-align: center;
|
||||
z-index: 24;
|
||||
cursor: pointer;
|
||||
color: #444;
|
||||
line-height: 50px;
|
||||
font-size: 0.8em;
|
||||
font-weight: 600;
|
||||
border: 1px dashed grey;
|
||||
|
||||
&.has-data {
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
&.has-subtitle {
|
||||
line-height: 38px;
|
||||
|
||||
.node-info-icon {
|
||||
top: -22px;
|
||||
|
||||
&.data-count {
|
||||
top: -15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
color: #a0a0a0;
|
||||
text-decoration: line-through;
|
||||
|
@ -244,7 +224,7 @@ export default mixins(nodeBase, workflowHelpers).extend({
|
|||
border-color: $--color-primary !important;
|
||||
|
||||
.node-executing-info {
|
||||
display: initial;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,72 +238,44 @@ export default mixins(nodeBase, workflowHelpers).extend({
|
|||
}
|
||||
}
|
||||
|
||||
.node-edit {
|
||||
.node-description {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
font-size: 1.1em;
|
||||
color: #ccc;
|
||||
border-radius: 0 25px 25px 0;
|
||||
|
||||
&:hover {
|
||||
color: #00cc00;
|
||||
}
|
||||
|
||||
.svg-inline--fa {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.node-execute {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: -25px;
|
||||
width: 45px;
|
||||
line-height: 50px;
|
||||
font-size: 1.5em;
|
||||
text-align: right;
|
||||
z-index: 10;
|
||||
color: #aaa;
|
||||
|
||||
.execute-icon:hover {
|
||||
color: $--color-primary;
|
||||
}
|
||||
bottom: -70px;
|
||||
left: -50px;
|
||||
width: 200px;
|
||||
height: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.node-executing-info {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: -35px;
|
||||
top: 8px;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
z-index: 12;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
font-size: 18px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 3.75em;
|
||||
line-height: 1.65em;
|
||||
text-align: center;
|
||||
border-radius: 15px;
|
||||
background-color: $--color-primary-light;
|
||||
color: $--color-primary;
|
||||
color: rgba($--color-primary, 0.7);
|
||||
}
|
||||
|
||||
.node-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 30px;
|
||||
margin: 10px;
|
||||
top: calc(50% - 30px);
|
||||
left: calc(50% - 30px);
|
||||
}
|
||||
|
||||
.node-info-icon {
|
||||
position: absolute;
|
||||
top: -28px;
|
||||
right: 18px;
|
||||
top: -18px;
|
||||
right: 12px;
|
||||
z-index: 10;
|
||||
|
||||
&.data-count {
|
||||
top: -22px;
|
||||
font-weight: 600;
|
||||
top: -12px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -338,14 +290,13 @@ export default mixins(nodeBase, workflowHelpers).extend({
|
|||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0 37px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.node-subtitle {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin: -23px 20px 0 20px;
|
||||
font-weight: 400;
|
||||
color: $--custom-font-light;
|
||||
font-size: 0.9em;
|
||||
|
@ -354,24 +305,29 @@ export default mixins(nodeBase, workflowHelpers).extend({
|
|||
.node-options {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: -28px;
|
||||
width: 45px;
|
||||
top: -8px;
|
||||
line-height: 1.8em;
|
||||
font-size: 12px;
|
||||
top: -35px;
|
||||
left: -10px;
|
||||
width: 120px;
|
||||
height: 45px;
|
||||
font-size: 1em;
|
||||
text-align: left;
|
||||
z-index: 10;
|
||||
color: #aaa;
|
||||
text-align: center;
|
||||
|
||||
.option {
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
padding: 0 0.3em;
|
||||
|
||||
&:hover {
|
||||
color: $--color-primary;
|
||||
}
|
||||
&.indent {
|
||||
margin-left: 7px;
|
||||
|
||||
.execute-icon {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="node-icon-wrapper">
|
||||
<div class="node-icon-wrapper" :style="iconStyleData">
|
||||
<div v-if="nodeIconData !== null" class="icon">
|
||||
<img :src="nodeIconData.path" style="width: 100%; height: 100%;" v-if="nodeIconData.type === 'file'"/>
|
||||
<font-awesome-icon :icon="nodeIconData.path" v-else-if="nodeIconData.type === 'fa'" />
|
||||
|
@ -23,8 +23,24 @@ export default Vue.extend({
|
|||
name: 'NodeIcon',
|
||||
props: [
|
||||
'nodeType',
|
||||
'size',
|
||||
],
|
||||
computed: {
|
||||
iconStyleData (): object {
|
||||
if (!this.size) {
|
||||
return {};
|
||||
}
|
||||
|
||||
let size = parseInt(this.size, 10);
|
||||
|
||||
return {
|
||||
width: size + 'px',
|
||||
height: size + 'px',
|
||||
'font-size': Math.floor(parseInt(this.size, 10) * 0.6) + 'px',
|
||||
'line-height': size + 'px',
|
||||
'border-radius': Math.ceil(size / 2) + 'px',
|
||||
}
|
||||
},
|
||||
nodeIconData (): null | NodeIconData {
|
||||
if (this.nodeType === null) {
|
||||
return null;
|
||||
|
@ -57,22 +73,17 @@ export default Vue.extend({
|
|||
.node-icon-wrapper {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 20px;
|
||||
border-radius: 15px;
|
||||
color: #444;
|
||||
line-height: 30px;
|
||||
font-size: 1.1em;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
|
||||
.icon {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
font-size: 20px;
|
||||
|
||||
.node-icon-placeholder {
|
||||
font-size: 1.4em;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,17 +64,17 @@ export const nodeBase = mixins(nodeIndex).extend({
|
|||
const nodeConnectors: IConnectionsUi = {
|
||||
main: {
|
||||
input: {
|
||||
uuid: '-top',
|
||||
uuid: '-input',
|
||||
maxConnections: -1,
|
||||
endpoint: 'Rectangle',
|
||||
endpointStyle: { width: 24, height: 12, fill: '#555', stroke: '#555', strokeWidth: 0 },
|
||||
endpointStyle: { width: 12, height: 24, fill: '#555', stroke: '#555', strokeWidth: 0 },
|
||||
dragAllowedWhenFull: true,
|
||||
},
|
||||
output: {
|
||||
uuid: '-bottom',
|
||||
uuid: '-output',
|
||||
maxConnections: -1,
|
||||
endpoint: 'Dot',
|
||||
endpointStyle: { radius: 9, fill: '#555', outlineStroke: 'none' },
|
||||
endpointStyle: { radius: 11, fill: '#555', outlineStroke: 'none' },
|
||||
dragAllowedWhenFull: true,
|
||||
},
|
||||
},
|
||||
|
@ -94,30 +94,30 @@ export const nodeBase = mixins(nodeIndex).extend({
|
|||
} = {
|
||||
input: {
|
||||
1: [
|
||||
'Top',
|
||||
'Left',
|
||||
],
|
||||
2: [
|
||||
[0.3, 0, 0, -1],
|
||||
[0.7, 0, 0, -1],
|
||||
[0, 0.3, -1, 0],
|
||||
[0, 0.7, -1, 0],
|
||||
],
|
||||
3: [
|
||||
[0.25, 0, 0, -1],
|
||||
[0.5, 0, 0, -1],
|
||||
[0.75, 0, 0, -1],
|
||||
[0, 0.25, -1, 0],
|
||||
[0, 0.5, -1, 0],
|
||||
[0, 0.75, -1, 0],
|
||||
],
|
||||
},
|
||||
output: {
|
||||
1: [
|
||||
'Bottom',
|
||||
'Right',
|
||||
],
|
||||
2: [
|
||||
[0.3, 1, 0, 1],
|
||||
[0.7, 1, 0, 1],
|
||||
[1, 0.3, 1, 0],
|
||||
[1, 0.7, 1, 0],
|
||||
],
|
||||
3: [
|
||||
[0.25, 1, 0, 1],
|
||||
[0.5, 1, 0, 1],
|
||||
[0.75, 1, 0, 1],
|
||||
[1, 0.25, 1, 0],
|
||||
[1, 0.5, 1, 0],
|
||||
[1, 0.75, 1, 0],
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -59,6 +59,7 @@ import {
|
|||
faStop,
|
||||
faSun,
|
||||
faSync,
|
||||
faSyncAlt,
|
||||
faTable,
|
||||
faTasks,
|
||||
faTerminal,
|
||||
|
@ -117,6 +118,7 @@ library.add(faSpinner);
|
|||
library.add(faStop);
|
||||
library.add(faSun);
|
||||
library.add(faSync);
|
||||
library.add(faSyncAlt);
|
||||
library.add(faTable);
|
||||
library.add(faTasks);
|
||||
library.add(faTerminal);
|
||||
|
|
|
@ -428,13 +428,13 @@ export default mixins(
|
|||
if (lastSelectedNode !== null) {
|
||||
this.$store.commit('setActiveNode', lastSelectedNode.name);
|
||||
}
|
||||
} else if (e.key === 'ArrowDown' && e.shiftKey === true) {
|
||||
} else if (e.key === 'ArrowRight' && e.shiftKey === true) {
|
||||
// Select all downstream nodes
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
this.callDebounced('selectDownstreamNodes', 1000);
|
||||
} else if (e.key === 'ArrowDown') {
|
||||
} else if (e.key === 'ArrowRight') {
|
||||
// Set child node active
|
||||
const lastSelectedNode = this.$store.getters.lastSelectedNode;
|
||||
if (lastSelectedNode === null) {
|
||||
|
@ -448,13 +448,13 @@ export default mixins(
|
|||
}
|
||||
|
||||
this.callDebounced('nodeSelectedByName', 100, connections.main[0][0].node, false, true);
|
||||
} else if (e.key === 'ArrowUp' && e.shiftKey === true) {
|
||||
} else if (e.key === 'ArrowLeft' && e.shiftKey === true) {
|
||||
// Select all downstream nodes
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
this.callDebounced('selectUpstreamNodes', 1000);
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
} else if (e.key === 'ArrowLeft') {
|
||||
// Set parent node active
|
||||
const lastSelectedNode = this.$store.getters.lastSelectedNode;
|
||||
if (lastSelectedNode === null) {
|
||||
|
@ -474,7 +474,7 @@ export default mixins(
|
|||
}
|
||||
|
||||
this.callDebounced('nodeSelectedByName', 100, connections.main[0][0].node, false, true);
|
||||
} else if (['ArrowLeft', 'ArrowRight'].includes(e.key)) {
|
||||
} else if (['ArrowUp', 'ArrowDown'].includes(e.key)) {
|
||||
// Set sibling node as active
|
||||
|
||||
// Check first if it has a parent node
|
||||
|
@ -504,7 +504,7 @@ export default mixins(
|
|||
|
||||
// Get all the sibling nodes and their x positions to know which one to set active
|
||||
let siblingNode: INodeUi;
|
||||
let lastCheckedNodePosition = e.key === 'ArrowLeft' ? -99999999 : 99999999;
|
||||
let lastCheckedNodePosition = e.key === 'ArrowUp' ? -99999999 : 99999999;
|
||||
let nextSelectNode: string | null = null;
|
||||
for (const ouputConnections of connectionsParent.main) {
|
||||
for (const ouputConnection of ouputConnections) {
|
||||
|
@ -514,17 +514,17 @@ export default mixins(
|
|||
}
|
||||
siblingNode = this.$store.getters.nodeByName(ouputConnection.node);
|
||||
|
||||
if (e.key === 'ArrowLeft') {
|
||||
if (e.key === 'ArrowUp') {
|
||||
// Get the next node on the left
|
||||
if (siblingNode.position[0] <= lastSelectedNode.position[0] && siblingNode.position[0] > lastCheckedNodePosition) {
|
||||
if (siblingNode.position[1] <= lastSelectedNode.position[1] && siblingNode.position[1] > lastCheckedNodePosition) {
|
||||
nextSelectNode = siblingNode.name;
|
||||
lastCheckedNodePosition = siblingNode.position[0];
|
||||
lastCheckedNodePosition = siblingNode.position[1];
|
||||
}
|
||||
} else {
|
||||
// Get the next node on the right
|
||||
if (siblingNode.position[0] >= lastSelectedNode.position[0] && siblingNode.position[0] < lastCheckedNodePosition) {
|
||||
if (siblingNode.position[1] >= lastSelectedNode.position[1] && siblingNode.position[1] < lastCheckedNodePosition) {
|
||||
nextSelectNode = siblingNode.name;
|
||||
lastCheckedNodePosition = siblingNode.position[0];
|
||||
lastCheckedNodePosition = siblingNode.position[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -929,8 +929,8 @@ export default mixins(
|
|||
// If a node is active then add the new node directly after the current one
|
||||
// newNodeData.position = [activeNode.position[0], activeNode.position[1] + 60];
|
||||
newNodeData.position = this.getNewNodePosition(
|
||||
[lastSelectedNode.position[0], lastSelectedNode.position[1] + 50],
|
||||
[0, 55]
|
||||
[lastSelectedNode.position[0] + 180, lastSelectedNode.position[1]],
|
||||
[110, 0]
|
||||
);
|
||||
} else {
|
||||
// If no node is active find a free spot
|
||||
|
@ -1068,8 +1068,8 @@ export default mixins(
|
|||
const sourceNode = this.$store.getters.nodeByName(sourceNodeName);
|
||||
|
||||
// TODO: That should happen after each move (only the setConnector part)
|
||||
if (info.sourceEndpoint.anchor.lastReturnValue[1] >= info.targetEndpoint.anchor.lastReturnValue[1]) {
|
||||
// When the source is underneath the target it will make sure that
|
||||
if (info.sourceEndpoint.anchor.lastReturnValue[0] >= info.targetEndpoint.anchor.lastReturnValue[0]) {
|
||||
// When the source is before the target it will make sure that
|
||||
// the connection is clearer visible
|
||||
|
||||
// Use the Flowchart connector if the source is underneath the target
|
||||
|
@ -1088,7 +1088,7 @@ export default mixins(
|
|||
|
||||
// Change also the color to give an additional visual hint
|
||||
info.connection.setPaintStyle({ strokeWidth: 2, stroke: '#334455' });
|
||||
} else if (Math.abs(info.sourceEndpoint.anchor.lastReturnValue[0] - info.targetEndpoint.anchor.lastReturnValue[0]) < 30) {
|
||||
} else if (Math.abs(info.sourceEndpoint.anchor.lastReturnValue[1] - info.targetEndpoint.anchor.lastReturnValue[1]) < 30) {
|
||||
info.connection.setConnector(['Straight']);
|
||||
}
|
||||
|
||||
|
@ -1274,8 +1274,8 @@ export default mixins(
|
|||
__addConnection (connection: [IConnection, IConnection], addVisualConnection = false) {
|
||||
if (addVisualConnection === true) {
|
||||
const uuid: [string, string] = [
|
||||
`${this.getNodeIndex(connection[0].node)}-bottom${connection[0].index}`,
|
||||
`${this.getNodeIndex(connection[1].node)}-top${connection[1].index}`,
|
||||
`${this.getNodeIndex(connection[0].node)}-output${connection[0].index}`,
|
||||
`${this.getNodeIndex(connection[1].node)}-input${connection[1].index}`,
|
||||
];
|
||||
|
||||
// Create connections in DOM
|
||||
|
@ -1348,7 +1348,7 @@ export default mixins(
|
|||
|
||||
newNodeData.position = this.getNewNodePosition(
|
||||
[node.position[0] + 180, node.position[1]],
|
||||
[90, 0]
|
||||
[0, 110]
|
||||
);
|
||||
|
||||
await this.addNodes([newNodeData]);
|
||||
|
|
Loading…
Reference in a new issue