fileoverview import { Module } from 'vuex' ; import $ from 'jquery' ; import _ from 'lodash' ; import Node from '@/components/node/node' ; import Port from '@/components/port/port' ; import store , { RootState } from '@/store' ; import * as helper from '@/store/interaction/helper' ; import Edge from '@/components/edge/edge' ; import { InteractionState } from './types' ; import { areBoxesIntersected } from '@/common/util' ; import * as history from './history' ; import { HistoryInteractionEvent } from '@/store/history/types' ; const IS_MAC = navigator . appVersion . match ( /mac/i ) !== null ; let droppedNodes = [ ] ; const initialState = { isNodeDragging : false , isNodeListDragging : false , draggedNode : undefined , draggedPort : undefined , draggedX1 : 0 , draggedY1 : 0 , draggedX2 : 0 , draggedY2 : 0 , lastMouseX : 0 , lastMouseY : 0 , altPressed : false , shiftPressed : false , ctrlPressed : false , metaPressed : false , altHold : false , isSystemInVisMode : false , mouseupEdge : undefined , osCtrlKey : IS_MAC ? 'meta' : 'ctrl' , osCtrlKeyChar : IS_MAC ? '⌘' : '⌃' } ; const getters = { isAltPressed : ( state ) : boolean => { return state . altHold || state . altPressed ; } , isShiftPressed : ( state ) : boolean => { return state . shiftPressed ; } , isCtrlPressed : ( state ) : boolean => { return state . ctrlPressed ; } , numSelectedNodes : ( state ) : number => { return store . state . dataflow . nodes . filter ( node => node . isSelected ) . length ; } , /**
     * Gets all the selected nodes.
     */ selectedNodes : ( state ) : Node [ ] => { return store . state . dataflow . nodes . filter ( node => node . isSelected ) ; } , /**
     * Gets the focused node, the node with the highest focus score.
     */ focusNode : ( state ) : Node | null => { return helper . focusNode ( ) ; } , isDraggedNodeDroppable : ( state ) : boolean => { if ( ! state . draggedNode ) { return false ; } return ! state . draggedNode . getAllEdges ( ) . length && getters . numSelectedNodes ( state ) === 1 ; } } ; const mutations = { nodeListDragStarted : ( state ) => { state . isNodeListDragging = true ; state . mouseupEdge = undefined ; } , nodeListDragEnded : ( state ) => { state . isNodeListDragging = false ; } , nodeDragStarted : ( state , node ) => { state . isNodeDragging = true ; state . draggedNode = node ; state . mouseupEdge = undefined ; } , nodeDragEnded : ( state ) => { if ( state . mouseupEdge && getters . isDraggedNodeDroppable ( state ) ) { store . commit ( 'dataflow/insertNodeOnEdge' , { node : state . draggedNode , edge : state . mouseupEdge } ) ; } state . isNodeDragging = false ; state . draggedNode = undefined ; } , portDragStarted : ( state , port ) => { droppedNodes = [ ] ; state . draggedPort = port ; const $port = $ ( port . $el ) ; const portOffset = $port . offset ( ) as JQuery . Coordinates ; const portWidth = $port . width ( ) as number ; const portHeight = $port . height ( ) as number ; state . draggedX1 = portOffset . left + portWidth / 2 ; state . draggedY1 = portOffset . top + portHeight / 2 ; } , portDragged : ( state , p ) => { state . draggedX2 = p . x ; state . draggedY2 = p . y ; } , portDragEnded : ( state , port ) => { state . draggedPort = undefined ; if ( ! droppedNodes . length ) { return ; } const maxNodeLayer = _ . max ( droppedNodes . map ( droppedNode => droppedNode . layer ) ) ; const node = droppedNodes . filter ( droppedNode => droppedNode . layer === maxNodeLayer ) [ 0 ] ; store . commit ( 'dataflow/createEdge' , { port , node } ) ; droppedNodes = [ ] ; } , /**
     * Saves the dropped node in a list. This is to prevent overlapping nodes being dropped together.
     * The edge creation is handled in portDragEnded().
     */ dropPortOnNode : ( state , node ) => { droppedNodes . push ( node ) ; } , dropPortOnPort : ( state , port ) => { let sourcePort = state . draggedPort as Port ; let targetPort = port ; if ( sourcePort . isInput ) { [ sourcePort , targetPort ] = [ targetPort , sourcePort ] ; } store . commit ( 'dataflow/createEdge' , { sourcePort , targetPort } ) ; } , mouseupOnEdge : ( state , edge ) => { state . mouseupEdge = edge ; } , clearMouseupEdge : ( state ) => { state . mouseupEdge = undefined ; } , /**
     * Selects nodes that intersect the box on the canvas.
     */ selectNodesInBoxOnCanvas : ( state , box ) => { store . state . dataflow . nodes . forEach ( node => { if ( areBoxesIntersected ( box , node . getBoundingBox ( ) ) ) { node . select ( ) ; } } ) ; } , toggleAltHold : ( state , value ? ) => { if ( value === undefined ) { state . altHold = ! state . altHold ; } else { state . altHold = value ; } } , dragNode : ( state , { node , dx , dy } ) => { helper . dragSelectedNodes ( node , dx , dy ) ; } , moveNode : ( state , { node , dx , dy } ) => { helper . moveNode ( node , dx , dy ) ; } , moveNodes : ( state , { nodes , dx , dy } ) => { helper . moveNodes ( nodes , dx , dy ) ; } , clickNode : ( state , clicked ) => { helper . deselectAllNodes ( { exception : clicked } ) ; } , clickBackground : ( state ) => { helper . deselectAllNodes ( ) ; helper . reduceAllNodeActiveness ( ) ; helper . closeFlowsenseInput ( ) ; window . getSelection ( ) . removeAllRanges ( ) ; } , reduceAllNodeActiveness : ( state , clicked ) => { helper . reduceAllNodeActiveness ( { exception : clicked } ) ; } , trackMouseMove : ( state , point ) => { state . lastMouseX = point . x ; state . lastMouseY = point . y ; } , mousedown : ( state ) => { } , mouseup : ( state ) => { state . altPressed = false ; state . ctrlPressed = false ; state . metaPressed = false ; state . shiftPressed = false ; } , mousemove : ( state ) => { } , keydown : ( state , evt ) => { const key = ( evt . key as string ) . toLowerCase ( ) ; switch ( key ) { case 'control' : state . ctrlPressed = true ; break ; case 'shift' : state . shiftPressed = true ; break ; case 'alt' : state . altPressed = true ; break ; case 'meta' : state . metaPressed = true ; break ; default : if ( 'a' <= key && key <= 'z' ) { helper . keyStroke ( state , [ state . metaPressed ? 'meta' : '' , state . ctrlPressed ? 'ctrl' : '' , state . shiftPressed ? 'shift' : '' , key , ] . filter ( s => s !== '' ) . join ( '+' ) , evt ) ; } } } , keyup : ( state , evt ) => { const key = ( evt . key as string ) . toLowerCase ( ) ; switch ( true ) { case key === 'control' : state . ctrlPressed = false ; break ; case key === 'shift' : state . shiftPressed = false ; break ; case key === 'alt' : state . altPressed = false ; break ; case key === 'meta' : state . metaPressed = false ; break ; } } , startSystemVisMode : ( state ) => { state . isSystemInVisMode = true ; store . commit ( 'history/commit' , history . toggleSystemVisModeEvent ( true ) ) ; } , endSystemVisMode : ( state ) => { state . isSystemInVisMode = false ; store . commit ( 'history/commit' , history . toggleSystemVisModeEvent ( false ) ) ; } , toggleSystemVisMode : ( state ) => { state . isSystemInVisMode = ! state . isSystemInVisMode ; store . commit ( 'history/commit' , history . toggleSystemVisModeEvent ( state . isSystemInVisMode ) ) ; } , redo : ( state , evt ) => { history . redo ( state , evt ) ; } , undo : ( state , evt ) => { history . undo ( state , evt ) ; } } ; const interaction = { namespaced : true , state : initialState , getters , mutations } ; export default interaction ;