import { IEndpointOptions , INodeUi , XYPosition } from '@/Interface' ; import mixins from 'vue-typed-mixins' ; import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers' ; import { nodeIndex } from '@/components/mixins/nodeIndex' ; import { NODE_NAME_PREFIX , NO_OP_NODE_TYPE } from '@/constants' ; import * as CanvasHelpers from '@/views/canvasHelpers' ; import { Endpoint } from 'jsplumb' ; import { INodeTypeDescription , } from 'n8n-workflow' ; import { getStyleTokenValue } from '../helpers' ; export const nodeBase = mixins ( deviceSupportHelpers , nodeIndex ) . extend ( { mounted ( ) { if ( this . data !== null ) { this . __addNode ( this . data ) ; } } , computed : { data ( ) { return this . $store . getters . getNodeByName ( this . name ) ; } , nodeId ( ) { return NODE_NAME_PREFIX + this . nodeIndex ; } , nodeIndex ( ) { return this . $store . getters . getNodeIndex ( this . data . name ) . toString ( ) ; } , } , props : [ 'name' , 'instance' , 'isReadOnly' , 'isActive' , 'hideActions' , ] , methods : { __addInputEndpoints ( node , nodeTypeData ) { let index ; const indexData = { } ; nodeTypeData . inputs . forEach ( ( inputName , i ) => { if ( indexData . hasOwnProperty ( inputName ) ) { indexData [ inputName ] ++ ; } else { indexData [ inputName ] = 0 ; } index = indexData [ inputName ] ; const anchorPosition = CanvasHelpers . ANCHOR_POSITIONS . input [ nodeTypeData . inputs . length ] [ index ] ; const newEndpointData = { uuid : CanvasHelpers . getInputEndpointUUID ( this . nodeIndex , index ) , anchor : anchorPosition , maxConnections : - 1 , endpoint : 'Rectangle' , endpointStyle : CanvasHelpers . getInputEndpointStyle ( nodeTypeData , '--color-foreground-xdark' ) , endpointHoverStyle : CanvasHelpers . getInputEndpointStyle ( nodeTypeData , '--color-primary' ) , isSource : false , isTarget : ! this . isReadOnly && nodeTypeData . inputs . length > 1 , parameters : { nodeIndex : this . nodeIndex , type : inputName , index , } , enabled : ! this . isReadOnly , cssClass : 'rect-input-endpoint' , dragAllowedWhenFull : true , dropOptions : { tolerance : 'touch' , hoverClass : 'dropHover' , } , } ; if ( nodeTypeData . inputNames ) { newEndpointData . overlays = [ CanvasHelpers . getInputNameOverlay ( nodeTypeData . inputNames [ index ] ) , ] ; } const endpoint = this . instance . addEndpoint ( this . nodeId , newEndpointData ) ; endpoint . __meta = { nodeName : node . name , nodeId : this . nodeId , index : i , totalEndpoints : nodeTypeData . inputs . length , } ; } ) ; } , __addOutputEndpoints ( node , nodeTypeData ) { let index ; const indexData = { } ; nodeTypeData . outputs . forEach ( ( inputName , i ) => { if ( indexData . hasOwnProperty ( inputName ) ) { indexData [ inputName ] ++ ; } else { indexData [ inputName ] = 0 ; } index = indexData [ inputName ] ; const anchorPosition = CanvasHelpers . ANCHOR_POSITIONS . output [ nodeTypeData . outputs . length ] [ index ] ; const newEndpointData = { uuid : CanvasHelpers . getOutputEndpointUUID ( this . nodeIndex , index ) , anchor : anchorPosition , maxConnections : - 1 , endpoint : 'Dot' , endpointStyle : CanvasHelpers . getOutputEndpointStyle ( nodeTypeData , '--color-foreground-xdark' ) , endpointHoverStyle : CanvasHelpers . getOutputEndpointStyle ( nodeTypeData , '--color-primary' ) , isSource : true , isTarget : false , enabled : ! this . isReadOnly , parameters : { nodeIndex : this . nodeIndex , type : inputName , index , } , cssClass : 'dot-output-endpoint' , dragAllowedWhenFull : false , dragProxy : [ 'Rectangle' , { width : 1 , height : 1 , strokeWidth : 0 } ] , } ; if ( nodeTypeData . outputNames ) { newEndpointData . overlays = [ CanvasHelpers . getOutputNameOverlay ( nodeTypeData . outputNames [ index ] ) , ] ; } const endpoint = this . instance . addEndpoint ( this . nodeId , { ... newEndpointData } ) ; endpoint . __meta = { nodeName : node . name , nodeId : this . nodeId , index : i , totalEndpoints : nodeTypeData . outputs . length , } ; if ( ! this . isReadOnly ) { const plusEndpointData = { uuid : CanvasHelpers . getOutputEndpointUUID ( this . nodeIndex , index ) , anchor : anchorPosition , maxConnections : - 1 , endpoint : 'N8nPlus' , isSource : true , isTarget : false , enabled : ! this . isReadOnly , endpointStyle : { fill : getStyleTokenValue ( '--color-xdark' ) , outlineStroke : 'none' , hover : false , showOutputLabel : nodeTypeData . outputs . length === 1 , size : nodeTypeData . outputs . length >= 3 ? 'small' : 'medium' , hoverMessage : this . $locale . baseText ( 'nodeBase.clickToAddNodeOrDragToConnect' ) , } , endpointHoverStyle : { fill : getStyleTokenValue ( '--color-primary' ) , outlineStroke : 'none' , hover : true , } , parameters : { nodeIndex : this . nodeIndex , type : inputName , index , } , cssClass : 'plus-draggable-endpoint' , dragAllowedWhenFull : false , dragProxy : [ 'Rectangle' , { width : 1 , height : 1 , strokeWidth : 0 } ] , } ; const plusEndpoint = this . instance . addEndpoint ( this . nodeId , plusEndpointData ) ; plusEndpoint . __meta = { nodeName : node . name , nodeId : this . nodeId , index : i , totalEndpoints : nodeTypeData . outputs . length , } ; } } ) ; } , __makeInstanceDraggable ( node ) { this . instance . draggable ( this . nodeId , { grid : [ CanvasHelpers . GRID_SIZE , CanvasHelpers . GRID_SIZE ] , start : ( params ) => { if ( this . isReadOnly === true ) { return false ; } this . dragging = true ; if ( params . e && ! this . $store . getters . isNodeSelected ( this . data . name ) ) { this . instance . clearDragSelection ( ) ; this . $store . commit ( 'resetSelectedNodes' ) ; } this . $store . commit ( 'addActiveAction' , 'dragActive' ) ; return true ; } , stop : ( params ) => { this . dragging = false ; if ( this . $store . getters . isActionActive ( 'dragActive' ) ) { const moveNodes = this . $store . getters . getSelectedNodes . slice ( ) ; const selectedNodeNames = moveNodes . map ( ( node ) => node . name ) ; if ( ! selectedNodeNames . includes ( this . data . name ) ) { moveNodes . push ( this . data ) ; } let newNodePositon ; moveNodes . forEach ( ( node ) => { const nodeElement = `node-${ this . getNodeIndex ( node . name ) }` ; const element = document . getElementById ( nodeElement ) ; if ( element === null ) { return ; } newNodePositon = [ parseInt ( element . style . left ! . slice ( 0 , - 2 ) , 10 ) , parseInt ( element . style . top ! . slice ( 0 , - 2 ) , 10 ) , ] ; const updateInformation = { name : node . name , properties : { position : newNodePositon , } , } ; this . $store . commit ( 'updateNodeProperties' , updateInformation ) ; } ) ; this . $emit ( 'moved' , node ) ; } } , filter : '.node-description, .node-description .node-name, .node-description .node-subtitle' , } ) ; } , __addNode ( node ) { let nodeTypeData = this . $store . getters . nodeType ( node . type ) as INodeTypeDescription | null ; if ( ! nodeTypeData ) { nodeTypeData = this . $store . getters . nodeType ( NO_OP_NODE_TYPE ) as INodeTypeDescription ; } this . __addInputEndpoints ( node , nodeTypeData ) ; this . __addOutputEndpoints ( node , nodeTypeData ) ; this . __makeInstanceDraggable ( node ) ; } , touchEnd ( e ) { if ( this . isTouchDevice ) { if ( this . $store . getters . isActionActive ( 'dragActive' ) ) { this . $store . commit ( 'removeActiveAction' , 'dragActive' ) ; } } } , mouseLeftClick ( e ) { const path = e . path || ( e . composedPath && e . composedPath ( ) ) ; for ( let index = 0 ; index < path . length ; index ++ ) { if ( path [ index ] . className && typeof path [ index ] . className === 'string' && path [ index ] . className . includes ( 'no-select-on-click' ) ) { return ; } } if ( ! this . isTouchDevice ) { if ( this . $store . getters . isActionActive ( 'dragActive' ) ) { this . $store . commit ( 'removeActiveAction' , 'dragActive' ) ; } else { if ( this . isCtrlKeyPressed ( e ) === false ) { this . $emit ( 'deselectAllNodes' ) ; } if ( this . $store . getters . isNodeSelected ( this . data . name ) ) { this . $emit ( 'deselectNode' , this . name ) ; } else { this . $emit ( 'nodeSelected' , this . name ) ; } } } } , } , } ) ;