import { Component , Vue , Watch } from 'vue-property-decorator' ; import _ from 'lodash' ; import $ from 'jquery' ; import ns from '@/store/namespaces' ; import Node from '@/components/node/node' ; import Edge from '@/components/edge/edge' ; import PortPanel from '@/components/port-panel/port-panel' ; import ContextMenu from '@/components/context-menu/context-menu' ; import GlobalClick from '@/directives/global-click' ; import Package from '@/data/package/package' ; @ Component ( { components : { PortPanel , ContextMenu } , directives : { GlobalClick } } ) export default class Port extends Vue { public id ! : string ; public node ! : Node ; public isInput = false ; protected DATA_TYPE = '' ; protected isMultiple = false ; protected isActive = false ; protected isAttachable = false ; protected isConnectionChanged = false ; protected edges : Edge [ ] = [ ] ; protected package : Package = new Package ( ) ; protected hasInvalidConnection = false ; @ ns . interaction . Mutation ( 'portDragStarted' ) private portDragStarted ! : ( port : Port ) => void ; @ ns . interaction . Mutation ( 'portDragged' ) private portDragged ! : ( coordinates : Point ) => void ; @ ns . interaction . Mutation ( 'portDragEnded' ) private portDragEnded ! : ( port : Port ) => void ; @ ns . interaction . Mutation ( 'dropPortOnPort' ) private dropPortOnPort ! : ( port : Port ) => void ; @ ns . panels . Mutation ( 'mountPortPanel' ) private mountPortPanel ! : ( panel : Vue ) => void ; @ ns . contextMenu . Mutation ( 'mount' ) private mountContextMenu ! : ( menu : ContextMenu ) => void ; @ ns . dataflow . Mutation ( 'disconnectPort' ) private disconnectPort ! : ( port : Port ) => void ; abstract public getConnectedNodes ( ) { console . error ( 'using getConnectedNodes() of base class Port' ) ; return [ ] ; } abstract public getConnectedPorts ( ) { console . error ( 'using getConnectedPorts() of base class Port' ) ; return [ ] ; } public dataType ( ) { return this . DATA_TYPE ; } public isConnected ( ) { return this . edges . length > 0 ; } public isTypeMatched ( port ) { return { matched : this . DATA_TYPE === port . DATA_TYPE , reason : 'port types do not match' } ; } public hasCapacity ( ) { return this . edges . length < this . maxConnections ; } public updateCoordinates ( ) { for ( const edge of this . edges ) { edge . updateCoordinates ( ) ; } } public getCenterCoordinates ( ) { const rect = ( this . $refs . port as HTMLElement ) . getBoundingClientRect ( ) ; return { x : rect . left + rect . width / 2 , y : rect . top + rect . height / 2 } ; } public getAllEdges ( ) { return _ . clone ( this . edges ) ; } /** Adds an edge to this port's incident list */ public addIncidentEdge ( edge ) { this . edges . push ( edge ) ; this . isConnectionChanged = true ; } /** Removes an edge from this port's incident list */ public removeIncidentEdge ( edge ) { this . edges = _ . clone ( _ . pull ( this . edges , edge ) ) ; this . isConnectionChanged = true ; } abstract public isPackageUpdated ( ) { console . error ( 'using isPackageUpdated() of base class Port' ) ; return false ; } abstract public hasPackage ( ) { console . error ( 'using hasPackage() of base class Port' ) ; return false ; } abstract public getPackage ( ) { console . error ( 'using getPackage() of base class Port' ) ; return new Package ( ) ; } /** Checks if edges have been changed. */ public isConnectionUpdated ( ) { return this . isConnectionChanged ; } /**
     * Checks if the connected edges still satisfy the type matching.
     * Though edges are type matched when created, it is possible that upflow changes data type due to data mutation
     * extensions.
     */ public checkValidConnections ( ) { console . error ( 'using checkValidConnections() of base class Port' ) ; return true ; } /** Clears the connection changed flag. */ public clearConnectionUpdate ( ) { if ( this . isConnectionChanged ) { Watch this . onConnectionChange ( ) ; } this . isConnectionChanged = false ; } /** Generates a tooltip string for the port. */ protected tooltip ( ) { return 'no data' ; } protected get iconClasses ( ) : string { return this . maxConnections === 1 ? 'fas fa-circle' : 'fas fa-caret-right' ; } protected onConnectionChange ( ) { } protected get maxConnections ( ) : number { return this . isMultiple ? Infinity : 1 ; } private activate ( ) { this . isActive = true ; this . node . deactivate ( ) ; this . $nextTick ( ( ) => this . mountPortPanel ( this . $refs . portPanel as Vue ) ) ; } private deactivate ( ) { this . isActive = false ; } /** Removes all incident edges */ private disconnect ( ) { this . disconnectPort ( this ) ; } private mousedown ( evt ) { if ( this . isActive ) { evt . stopPropagation ( ) ; } } private clickLink ( ) { console . log ( 'link clicked' , this . id ) ; } private globalClick ( evt ) { if ( ( this . $refs . port as Element ) . contains ( evt . target as Element ) || this . $refs . portPanel && ( this . $refs . portPanel as Vue ) . $el . contains ( evt . target as Element ) ) { return ; } this . deactivate ( ) ; } private openContextMenu ( evt ) { ( this . $refs . contextMenu as ContextMenu ) . open ( evt ) ; } private mounted ( ) { this . mountContextMenu ( this . $refs . contextMenu as ContextMenu ) ; const $port = $ ( this . $refs . port ) ; $port . draggable ( { helper : ( ) => $ ( '<div></div>' ) , start : ( ) => this . portDragStarted ( this ) , drag : ( evt_ ) => { const evt = evt_ as MouseEvent ; this . portDragged ( { x : evt . pageX , y : evt . pageY } ) ; } , stop : ( ) => this . portDragEnded ( this ) } ) ; $port . droppable ( { hoverClass : 'hover' , tolerance : 'pointer' , accept : '.port' , drop : ( evt , ui ) => { this . dropPortOnPort ( this ) ; } } ) ; } }