2019-06-23 03:35:23 -07:00
< template >
2021-10-27 04:39:07 -07:00
< div class = "node-wrapper" :style ="nodePosition" >
< div : class = "{'selected': true, 'has-subtitles': !!nodeSubtitle}" v-show ="isSelected" > < / div >
2021-08-08 02:14:09 -07:00
< div class = "node-default" :ref ="data.name" :style ="nodeStyle" :class ="nodeClass" @dblclick ="setNodeActive" @click.left ="mouseLeftClick" v -touch :start ="touchStart" v -touch :end ="touchEnd" >
2019-07-25 12:57:27 -07:00
< div v-if ="hasIssues" class="node-info-icon node-issues" >
2021-08-29 04:36:17 -07:00
< n8n -tooltip placement = "top" >
2019-07-25 12:57:27 -07:00
< div slot = "content" v-html ="nodeIssues" > < / div >
< font -awesome -icon icon = "exclamation-triangle" / >
2021-08-29 04:36:17 -07:00
< / n 8 n - t o o l t i p >
2019-07-25 09:50:45 -07:00
< / div >
2021-10-27 03:07:52 -07:00
< span v -else -if = " workflowDataItems & & ! data.disabled " class = "node-info-icon data-count" >
< font -awesome -icon icon = "check" / >
< span v-if ="workflowDataItems > 1" > {{ workflowDataItems }} < / span >
< / span >
2019-07-25 12:57:27 -07:00
2021-10-27 03:07:52 -07:00
< div v-if ="waiting" class="waiting" >
2021-08-29 04:36:17 -07:00
< n8n -tooltip placement = "top" >
2021-08-21 05:11:32 -07:00
< div slot = "content" v-html ="waiting" > < / div >
< font -awesome -icon icon = "clock" / >
2021-08-29 04:36:17 -07:00
< / n 8 n - t o o l t i p >
2021-08-21 05:11:32 -07:00
< / div >
2019-07-25 12:57:27 -07:00
< div class = "node-executing-info" title = "Node is executing" >
< font -awesome -icon icon = "sync-alt" spin / >
2019-06-23 03:35:23 -07:00
< / div >
2020-12-23 23:37:13 -08:00
< div class = "node-options no-select-on-click" v-if ="!isReadOnly" >
2020-10-23 04:44:34 -07:00
< div v -touch :tap ="deleteNode" class = "option" title = "Delete Node" >
2019-07-25 12:57:27 -07:00
< font -awesome -icon icon = "trash" / >
< / div >
2020-10-24 11:01:13 -07:00
< div v -touch :tap ="disableNode" class = "option" title = "Activate/Deactivate Node" >
2019-07-25 12:57:27 -07:00
< font -awesome -icon :icon ="nodeDisabledIcon" / >
< / div >
2020-10-23 04:44:34 -07:00
< div v -touch :tap ="duplicateNode" class = "option" title = "Duplicate Node" >
2019-07-25 12:57:27 -07:00
< font -awesome -icon icon = "clone" / >
< / div >
2020-10-23 04:44:34 -07:00
< div v -touch :tap ="setNodeActive" class = "option touch" title = "Edit Node" v-if ="!isReadOnly" >
2020-02-15 17:29:08 -08:00
< font -awesome -icon class = "execute-icon" icon = "cog" / >
< / div >
2020-10-23 04:44:34 -07:00
< div v -touch :tap ="executeNode" class = "option" title = "Execute Node" v-if ="!isReadOnly && !workflowRunning" >
2019-07-25 12:57:27 -07:00
< font -awesome -icon class = "execute-icon" icon = "play-circle" / >
< / div >
2019-06-23 03:35:23 -07:00
< / div >
2019-07-25 12:57:27 -07:00
2021-10-27 03:07:52 -07:00
< NodeIcon class = "node-icon" :nodeType ="nodeType" :size ="40" :shrink ="false" :disabled ="this.data.disabled" / >
2019-06-23 03:35:23 -07:00
< / div >
2021-10-27 05:04:06 -07:00
< div : class = "{'node-description': true, 'limited': isSelected}" >
2019-07-25 09:50:45 -07:00
< div class = "node-name" :title ="data.name" >
{ { data . name } }
< / div >
< div v-if ="nodeSubtitle !== undefined" class="node-subtitle" :title ="nodeSubtitle" >
{ { nodeSubtitle } }
< / div >
2019-06-23 03:35:23 -07:00
< / div >
< / div >
< / template >
< script lang = "ts" >
import Vue from 'vue' ;
2021-08-21 05:11:32 -07:00
import { WAIT _TIME _UNLIMITED } from '@/constants' ;
2021-06-18 13:47:03 -07:00
import { externalHooks } from '@/components/mixins/externalHooks' ;
2019-06-23 03:35:23 -07:00
import { nodeBase } from '@/components/mixins/nodeBase' ;
2021-05-05 17:46:33 -07:00
import { nodeHelpers } from '@/components/mixins/nodeHelpers' ;
2019-07-12 05:14:36 -07:00
import { workflowHelpers } from '@/components/mixins/workflowHelpers' ;
2019-06-23 03:35:23 -07:00
import {
INodeTypeDescription ,
2021-10-18 05:53:56 -07:00
ITaskData ,
2019-06-23 03:35:23 -07:00
NodeHelpers ,
} from 'n8n-workflow' ;
import NodeIcon from '@/components/NodeIcon.vue' ;
import mixins from 'vue-typed-mixins' ;
2021-08-21 05:11:32 -07:00
import { get } from 'lodash' ;
2021-10-27 03:07:52 -07:00
import { getStyleTokenValue } from './helpers' ;
2021-10-27 04:39:07 -07:00
import { INodeUi } from '@/Interface' ;
2021-08-21 05:11:32 -07:00
2021-06-18 13:47:03 -07:00
export default mixins ( externalHooks , nodeBase , nodeHelpers , workflowHelpers ) . extend ( {
2019-06-23 03:35:23 -07:00
name : 'Node' ,
components : {
NodeIcon ,
} ,
computed : {
2021-10-18 05:53:56 -07:00
nodeRunData ( ) : ITaskData [ ] {
return this . $store . getters . getWorkflowResultDataByNodeName ( this . data . name ) ;
} ,
2021-10-18 07:00:38 -07:00
workflowDataItems ( ) : number {
2021-10-18 05:53:56 -07:00
const workflowResultDataNode = this . nodeRunData ;
2019-06-24 01:28:18 -07:00
if ( workflowResultDataNode === null ) {
2019-06-23 03:35:23 -07:00
return 0 ;
}
2019-06-24 01:28:18 -07:00
return workflowResultDataNode . length ;
2019-06-23 03:35:23 -07:00
} ,
isExecuting ( ) : boolean {
return this . $store . getters . executingNode === this . data . name ;
} ,
nodeType ( ) : INodeTypeDescription | null {
return this . $store . getters . nodeType ( this . data . type ) ;
} ,
nodeClass ( ) {
const classes = [ ] ;
if ( this . data . disabled ) {
classes . push ( 'disabled' ) ;
}
if ( this . isExecuting ) {
classes . push ( 'executing' ) ;
}
if ( this . workflowDataItems !== 0 ) {
classes . push ( 'has-data' ) ;
}
2019-07-25 22:49:00 -07:00
if ( this . hasIssues ) {
classes . push ( 'has-issues' ) ;
}
2020-02-15 17:29:08 -08:00
if ( this . isTouchDevice ) {
classes . push ( 'is-touch-device' ) ;
}
2020-10-24 11:01:13 -07:00
if ( this . isTouchActive ) {
classes . push ( 'touch-active' ) ;
}
2019-06-23 03:35:23 -07:00
return classes ;
} ,
nodeIssues ( ) : string {
if ( this . data . issues === undefined ) {
return '' ;
}
const nodeIssues = NodeHelpers . nodeIssuesToString ( this . data . issues , this . data ) ;
return 'Issues:<br /> - ' + nodeIssues . join ( '<br /> - ' ) ;
} ,
nodeDisabledIcon ( ) : string {
if ( this . data . disabled === false ) {
return 'pause' ;
} else {
return 'play' ;
}
} ,
2021-08-21 05:11:32 -07:00
waiting ( ) : string | undefined {
const workflowExecution = this . $store . getters . getWorkflowExecution ;
if ( workflowExecution && workflowExecution . waitTill ) {
const lastNodeExecuted = get ( workflowExecution , 'data.resultData.lastNodeExecuted' ) ;
if ( this . name === lastNodeExecuted ) {
const waitDate = new Date ( workflowExecution . waitTill ) ;
if ( waitDate . toISOString ( ) === WAIT _TIME _UNLIMITED ) {
return 'The node is waiting indefinitely for an incoming webhook call.' ;
}
return ` Node is waiting till ${ waitDate . toLocaleDateString ( ) } ${ waitDate . toLocaleTimeString ( ) } ` ;
}
}
return ;
} ,
2019-06-23 03:35:23 -07:00
workflowRunning ( ) : boolean {
return this . $store . getters . isActionActive ( 'workflowRunning' ) ;
} ,
2021-10-27 03:07:52 -07:00
nodeStyle ( ) : object {
let borderColor = getStyleTokenValue ( '--color-foreground-xdark' ) ;
2021-10-27 03:22:17 -07:00
if ( this . data . disabled ) {
borderColor = getStyleTokenValue ( '--color-foreground-base' ) ;
}
else if ( this . hasIssues && this . workflowDataItems ) {
2021-10-27 03:07:52 -07:00
borderColor = getStyleTokenValue ( '--color-danger' ) ;
}
else if ( this . workflowDataItems && ! this . isExecuting ) {
borderColor = getStyleTokenValue ( '--color-success' ) ;
}
const returnStyles : {
[ key : string ] : string ;
} = {
'border-color' : borderColor ,
} ;
return returnStyles ;
} ,
2021-10-27 04:39:07 -07:00
isSelected ( ) : boolean {
return this . $store . getters . getSelectedNodes . find ( ( node : INodeUi ) => node . name === this . data . name ) ;
} ,
2021-08-07 00:35:59 -07:00
} ,
watch : {
isActive ( newValue , oldValue ) {
if ( ! newValue && oldValue ) {
this . setSubtitle ( ) ;
}
2019-07-12 05:14:36 -07:00
} ,
2021-10-18 05:53:56 -07:00
nodeRunData ( newValue ) {
this . $emit ( 'run' , { name : this . data . name , data : newValue } ) ;
} ,
2019-06-23 03:35:23 -07:00
} ,
2021-08-07 00:35:59 -07:00
mounted ( ) {
this . setSubtitle ( ) ;
2021-10-18 05:53:56 -07:00
setTimeout ( ( ) => {
this . $emit ( 'run' , { name : this . data . name , data : this . nodeRunData } ) ;
} , 0 ) ;
2021-08-07 00:35:59 -07:00
} ,
2019-06-23 03:35:23 -07:00
data ( ) {
return {
2020-10-24 11:01:13 -07:00
isTouchActive : false ,
2021-08-07 00:35:59 -07:00
nodeSubtitle : '' ,
2019-06-23 03:35:23 -07:00
} ;
} ,
methods : {
2021-08-07 00:35:59 -07:00
setSubtitle ( ) {
this . nodeSubtitle = this . getNodeSubtitle ( this . data , this . nodeType , this . getWorkflow ( ) ) || '' ;
} ,
2019-06-23 03:35:23 -07:00
disableNode ( ) {
2020-02-09 23:18:44 -08:00
this . disableNodes ( [ this . data ] ) ;
2021-10-18 20:57:49 -07:00
this . $telemetry . track ( 'User set node enabled status' , { node _type : this . data . type , is _enabled : ! this . data . disabled , workflow _id : this . $store . getters . workflowId } ) ;
2019-06-23 03:35:23 -07:00
} ,
executeNode ( ) {
2021-05-05 17:46:33 -07:00
this . $emit ( 'runWorkflow' , this . data . name , 'Node.executeNode' ) ;
2019-06-23 03:35:23 -07:00
} ,
deleteNode ( ) {
2021-06-18 13:47:03 -07:00
this . $externalHooks ( ) . run ( 'node.deleteNode' , { node : this . data } ) ;
2021-10-18 20:57:49 -07:00
this . $telemetry . track ( 'User deleted node' , { node _type : this . data . type , workflow _id : this . $store . getters . workflowId } ) ;
2019-06-23 03:35:23 -07:00
Vue . nextTick ( ( ) => {
// Wait a tick else vue causes problems because the data is gone
this . $emit ( 'removeNode' , this . data . name ) ;
} ) ;
} ,
duplicateNode ( ) {
Vue . nextTick ( ( ) => {
// Wait a tick else vue causes problems because the data is gone
this . $emit ( 'duplicateNode' , this . data . name ) ;
} ) ;
} ,
setNodeActive ( ) {
this . $store . commit ( 'setActiveNode' , this . data . name ) ;
} ,
2020-10-24 11:01:13 -07:00
touchStart ( ) {
if ( this . isTouchDevice === true && this . isMacOs === false && this . isTouchActive === false ) {
this . isTouchActive = true ;
setTimeout ( ( ) => {
this . isTouchActive = false ;
} , 2000 ) ;
}
} ,
2019-06-23 03:35:23 -07:00
} ,
} ) ;
< / script >
2021-10-27 04:24:04 -07:00
< style lang = "scss" scoped >
2019-06-23 03:35:23 -07:00
2019-07-25 12:57:27 -07:00
. node - wrapper {
2019-06-23 03:35:23 -07:00
position : absolute ;
2019-07-25 09:50:45 -07:00
width : 100 px ;
height : 100 px ;
2019-06-23 03:35:23 -07:00
2019-07-25 09:50:45 -07:00
. node - description {
2021-08-29 04:36:17 -07:00
line - height : 1.5 ;
2019-06-23 03:35:23 -07:00
position : absolute ;
2019-07-25 12:57:27 -07:00
bottom : - 55 px ;
2019-07-25 09:50:45 -07:00
left : - 50 px ;
width : 200 px ;
2019-07-25 12:57:27 -07:00
height : 50 px ;
2019-07-25 09:50:45 -07:00
text - align : center ;
2019-07-25 12:57:27 -07:00
cursor : default ;
2021-10-27 05:04:06 -07:00
& . limited {
padding : 0 50 px ;
}
2019-07-25 12:57:27 -07:00
. node - name {
white - space : nowrap ;
overflow : hidden ;
text - overflow : ellipsis ;
2021-10-27 05:04:06 -07:00
font - weight : var ( -- font - weight - bold ) ;
2019-07-25 12:57:27 -07:00
}
. node - subtitle {
white - space : nowrap ;
overflow : hidden ;
text - overflow : ellipsis ;
font - weight : 400 ;
color : $ -- custom - font - light ;
font - size : 0.8 em ;
}
2019-06-23 03:35:23 -07:00
}
2019-07-25 12:57:27 -07:00
. node - default {
2021-10-27 04:24:04 -07:00
position : absolute ;
2019-07-25 09:50:45 -07:00
width : 100 % ;
height : 100 % ;
2019-07-25 12:57:27 -07:00
background - color : # fff ;
2021-10-27 03:07:52 -07:00
border - radius : var ( -- border - radius - large ) ;
2019-06-23 03:35:23 -07:00
text - align : center ;
2019-07-25 12:57:27 -07:00
cursor : pointer ;
color : # 444 ;
2021-10-27 03:07:52 -07:00
border : 2 px solid var ( -- color - foreground - xdark ) ;
2019-06-23 03:35:23 -07:00
2019-07-25 12:57:27 -07:00
& . has - data {
border - style : solid ;
}
2019-06-23 03:35:23 -07:00
2019-07-25 12:57:27 -07:00
& . executing {
background - color : $ -- color - primary - light ! important ;
2019-06-23 03:35:23 -07:00
2019-07-25 12:57:27 -07:00
. node - executing - info {
display : inline - block ;
}
2019-06-23 03:35:23 -07:00
}
2020-10-24 11:01:13 -07:00
& . touch - active ,
2019-07-25 12:57:27 -07:00
& : hover {
. node - execute {
display : initial ;
}
2019-06-23 03:35:23 -07:00
2019-07-25 12:57:27 -07:00
. node - options {
display : initial ;
}
}
2019-06-23 03:35:23 -07:00
2019-07-25 12:57:27 -07:00
. node - executing - info {
display : none ;
position : absolute ;
left : 0 px ;
top : 0 px ;
z - index : 12 ;
width : 100 % ;
height : 100 % ;
font - size : 3.75 em ;
line - height : 1.65 em ;
text - align : center ;
color : rgba ( $ -- color - primary , 0.7 ) ;
}
2019-06-23 03:35:23 -07:00
2019-07-25 12:57:27 -07:00
. node - icon {
position : absolute ;
2021-10-27 03:07:52 -07:00
top : calc ( 50 % - 20 px ) ;
left : calc ( 50 % - 20 px ) ;
2019-07-25 12:57:27 -07:00
}
2019-06-23 03:35:23 -07:00
2019-07-25 12:57:27 -07:00
. node - info - icon {
position : absolute ;
2021-10-27 03:07:52 -07:00
bottom : 8 px ;
right : 8 px ;
2019-06-23 03:35:23 -07:00
2019-07-25 12:57:27 -07:00
& . data - count {
font - weight : 600 ;
2021-10-27 03:07:52 -07:00
color : var ( -- color - success ) ;
2021-08-21 05:11:32 -07:00
2021-10-27 03:07:52 -07:00
* {
margin - left : 2 px ;
}
2021-08-21 05:11:32 -07:00
}
2019-07-25 09:50:45 -07:00
2021-10-27 03:07:52 -07:00
& . node - issues {
color : var ( -- color - danger ) ;
}
2019-07-25 12:57:27 -07:00
}
2021-08-21 05:11:32 -07:00
. waiting {
2021-10-27 03:07:52 -07:00
color : var ( -- color - secondary ) ;
position : absolute ;
left : 8 px ;
bottom : 8 px ;
2021-08-21 05:11:32 -07:00
}
2019-07-25 12:57:27 -07:00
. node - options {
display : none ;
position : absolute ;
2019-07-25 22:49:00 -07:00
top : - 25 px ;
2019-07-25 12:57:27 -07:00
left : - 10 px ;
width : 120 px ;
height : 45 px ;
font - size : 0.9 em ;
text - align : left ;
z - index : 10 ;
color : # aaa ;
text - align : center ;
. option {
2021-08-29 04:36:17 -07:00
width : 28 px ;
2019-07-25 12:57:27 -07:00
display : inline - block ;
2020-02-15 17:29:08 -08:00
& . touch {
display : none ;
}
2019-07-25 12:57:27 -07:00
& : hover {
color : $ -- color - primary ;
}
. execute - icon {
position : relative ;
top : 2 px ;
font - size : 1.2 em ;
}
2019-06-23 03:35:23 -07:00
}
}
2019-07-25 22:49:00 -07:00
2020-02-15 17:29:08 -08:00
& . is - touch - device . node - options {
left : - 25 px ;
width : 150 px ;
2020-02-09 00:06:01 -08:00
2020-02-15 17:29:08 -08:00
. option . touch {
display : initial ;
2020-02-09 00:06:01 -08:00
}
}
2019-06-23 03:35:23 -07:00
}
}
2021-10-27 04:24:04 -07:00
. selected {
2021-10-27 04:39:07 -07:00
content : ' ' ;
display : block ;
2021-10-27 04:24:04 -07:00
position : absolute ;
top : - 8 px ;
left : - 8 px ;
2021-11-02 02:19:50 -07:00
background - color : hsla ( var ( -- color - foreground - base - h ) , var ( -- color - foreground - base - s ) , var ( -- color - foreground - base - l , 50 % ) ) ;
height : 144 px ;
2021-10-27 04:24:04 -07:00
width : 116 px ;
border - radius : var ( -- border - radius - xlarge ) ;
opacity : 0.8 ;
z - index : 0 ;
2021-10-27 04:39:07 -07:00
& . has - subtitles {
2021-11-02 02:19:50 -07:00
height : 162 px ;
2021-10-27 04:39:07 -07:00
}
2021-10-27 04:24:04 -07:00
}
2019-06-23 03:35:23 -07:00
< / style >
2021-10-27 04:24:04 -07:00
< style lang = "scss" >
2019-06-23 03:35:23 -07:00
. el - badge _ _content {
border - width : 2 px ;
background - color : # 67 c23a ;
}
2021-10-28 07:46:30 -07:00
/** node */
. jtk - endpoint - anchor . node - default {
z - index : 2 ;
}
2021-10-20 03:59:09 -07:00
/** connector */
2019-06-23 03:35:23 -07:00
. jtk - connector {
2021-10-27 04:24:04 -07:00
z - index : 3 ;
2019-06-23 03:35:23 -07:00
}
2021-10-13 05:05:26 -07:00
2021-10-22 06:21:29 -07:00
. jtk - connector path {
transition : stroke .1 s ease - in - out ;
}
2021-10-29 00:38:04 -07:00
. jtk - connector . success {
z - index : 4 ;
}
2021-10-20 03:59:09 -07:00
/** node endpoints */
2019-06-23 03:35:23 -07:00
. jtk - endpoint {
z - index : 5 ;
}
2021-10-20 03:59:09 -07:00
. jtk - connector . jtk - hover {
z - index : 6 ;
}
2021-10-26 07:36:38 -07:00
. jtk - endpoint . jtk - hover {
z - index : 7 ;
}
2019-06-23 03:35:23 -07:00
. jtk - overlay {
2021-10-20 07:36:17 -07:00
z - index : 7 ;
}
. jtk - connector . jtk - dragging {
z - index : 8 ;
2019-06-23 03:35:23 -07:00
}
2021-10-22 01:31:19 -07:00
. jtk - endpoint . jtk - drag - active {
z - index : 9 ;
}
2019-06-23 03:35:23 -07:00
. disabled . node - icon img {
- webkit - filter : contrast ( 40 % ) brightness ( 1.5 ) grayscale ( 100 % ) ;
filter : contrast ( 40 % ) brightness ( 1.5 ) grayscale ( 100 % ) ;
}
< / style >