|
@@ -8,10 +8,12 @@
|
|
|
:class="{
|
|
|
'flow-linking':linking || stickySockets,
|
|
|
'activity':dragging || pointerLink.active ,
|
|
|
- 'flow-detail': detailed
|
|
|
+ 'flow-detail': detailed,
|
|
|
+ 'selecting': (selector)?true:false
|
|
|
}"
|
|
|
@dragover.prevent
|
|
|
@drop="managerDrop"
|
|
|
+ @mousedown="viewPointerDown"
|
|
|
:width="width"
|
|
|
:height="height">
|
|
|
<svg-defs/>
|
|
@@ -34,9 +36,10 @@
|
|
|
v-bind="nodeProps(n)"
|
|
|
:key="'node' + n.id"
|
|
|
:id="n.id"
|
|
|
- @nodePointerDown.prevent="nodeDragStart($event,i)"
|
|
|
+ :selected="nodeSelection[n.id]?true:false"
|
|
|
+ @nodePointerDown.prevent="nodePointerDown($event,i)"
|
|
|
@socketPointerDown="socketPointerDown(n.id,...arguments)"
|
|
|
- @nodeDoubleClick="$emit('nodeInspect',n.id)"
|
|
|
+ @nodeDoubleClick="$emit('nodeInspect',n)"
|
|
|
/>
|
|
|
<!-- mouse link-->
|
|
|
<flow-link
|
|
@@ -44,6 +47,10 @@
|
|
|
v-if="pointerLink.active"
|
|
|
v-bind="pointerLink.props"
|
|
|
/>
|
|
|
+ <rect
|
|
|
+ class="flow-selector"
|
|
|
+ :class="{'flow-selector--selecting':(selector)?true:false}"
|
|
|
+ v-bind="selector"/>
|
|
|
|
|
|
</flow-pan-zoom>
|
|
|
</svg>
|
|
@@ -71,7 +78,7 @@ export default {
|
|
|
name: 'FlowManager',
|
|
|
components: {FlowNode, FlowLink, FlowPanZoom, SvgDefs},
|
|
|
props: {
|
|
|
- 'value': {type: Object, default: () => {}},
|
|
|
+ // 'value': {type: Object, default: () => {}},
|
|
|
'registry': {type: Object, default: () => {}},
|
|
|
'width': {type: String, default: '800px'},
|
|
|
'height': {type: String, default: '600px'}
|
|
@@ -86,7 +93,9 @@ export default {
|
|
|
linking: false,
|
|
|
stickySockets: false,
|
|
|
detailed: false,
|
|
|
- pointerLink: {active: false, props: {}, src: {}}
|
|
|
+ pointerLink: {active: false, props: {}, src: {}},
|
|
|
+ selector: null,
|
|
|
+ nodeSelection: {}
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
@@ -161,6 +170,47 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
mounted () {
|
|
|
+ this.$parent.$on('sparta', () => {
|
|
|
+ console.log('This is weird')
|
|
|
+ })
|
|
|
+ this.$flowService.on('document', (v) => {
|
|
|
+ if (!v.data) { return }
|
|
|
+ const nodes = []
|
|
|
+ for (let n of v.data.nodes) {
|
|
|
+ if (this.dragging && this.dragging.id === n.id) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ nodes.push(n)
|
|
|
+ }
|
|
|
+ if (this.dragging) {
|
|
|
+ nodes.push(this.dragging)
|
|
|
+ }
|
|
|
+ this.nodeData = {
|
|
|
+ nodes: nodes,
|
|
|
+ links: v.data.links
|
|
|
+ }
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$forceUpdate()
|
|
|
+ })
|
|
|
+
|
|
|
+ this.nodeData = {
|
|
|
+ nodes: v.data.nodes || [],
|
|
|
+ links: v.data.links || []
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.$flowService.on('nodeUpdate', (v) => {
|
|
|
+ const nodes = v.data
|
|
|
+ const nd = this.nodeData
|
|
|
+ for (let node of nodes) {
|
|
|
+ const idx = nd.nodes.findIndex(n => n.id === node.id)
|
|
|
+ if (idx === -1) { // new node
|
|
|
+ nd.nodes.push(node)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ this.$set(nd.nodes, idx, node) // new Node
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
this.$nextTick(() => {
|
|
|
this.$forceUpdate()
|
|
|
})
|
|
@@ -191,6 +241,7 @@ export default {
|
|
|
// and create some LinkAdd method
|
|
|
socketPointerDown (nodeId, ev, socket) {
|
|
|
if (ev.button !== 0) return
|
|
|
+
|
|
|
const nodeRef = this.$refs.nodes.find(n => n.id === nodeId)
|
|
|
const node = this.nodeData.nodes.find(n => n.id === nodeId)
|
|
|
|
|
@@ -264,7 +315,7 @@ export default {
|
|
|
const output = this.registry[nodeFrom.src].output
|
|
|
const input = this.registry[nodeTo.src].inputs[link.in]
|
|
|
// Type checking
|
|
|
- if (!(output === 'any' || output == input || input === 'any')) {
|
|
|
+ if (!(output === 'any' || output === input || input === 'any')) {
|
|
|
console.error('LINK: Invalid type')
|
|
|
return
|
|
|
}
|
|
@@ -285,16 +336,16 @@ export default {
|
|
|
this.nodeModalTarget = i
|
|
|
this.nodeModal = true
|
|
|
},
|
|
|
- nodeDragStart (ev, i) {
|
|
|
+
|
|
|
+ nodePointerDown (ev, i) {
|
|
|
document.activeElement && document.activeElement.blur()
|
|
|
- var tnode = this.nodeData.nodes[i]
|
|
|
+ const tnode = this.nodeData.nodes[i]
|
|
|
if (ev.button === 1) {
|
|
|
// remove related links
|
|
|
this.nodeData.links = this.nodeData.links.filter(l => l.from !== tnode.id && l.to !== tnode.id)
|
|
|
this.nodeData.nodes.splice(i, 1)
|
|
|
- this.$emit('input', this.nodeData)
|
|
|
- this.$emit('nodeRemove', tnode)
|
|
|
- this.$emit('shouldPersist')
|
|
|
+ this.sendFlowEvent('nodeRemove', tnode)
|
|
|
+ this.sendDocumentUpdate()
|
|
|
return
|
|
|
}
|
|
|
if (ev.button !== 0) return // first button
|
|
@@ -302,32 +353,45 @@ export default {
|
|
|
this.socketPointerDown(tnode.id, ev, {out: 0})
|
|
|
return
|
|
|
}
|
|
|
+ // Switch selection
|
|
|
+ if (!this.nodeSelection[tnode.id] && !ev.ctrlKey) this.nodeSelection = {}
|
|
|
+ this.nodeSelection[tnode.id] = tnode
|
|
|
// we can handle with nodeId and a search
|
|
|
this.nodeData.nodes.splice(i, 1)
|
|
|
this.nodeData.nodes.push(tnode) // put in last
|
|
|
|
|
|
- this.dragging = tnode
|
|
|
// transform CTM
|
|
|
- const delta = this.transformedPoint(ev.clientX, ev.clientY)
|
|
|
- delta.x -= tnode.x
|
|
|
- delta.y -= tnode.y
|
|
|
+ // delta.x -= tnode.x
|
|
|
+ // delta.y -= tnode.y
|
|
|
+
|
|
|
+ const nodeSel = this.nodeData.nodes.filter(n => {
|
|
|
+ return !!this.nodeSelection[n.id]
|
|
|
+ })
|
|
|
|
|
|
+ let curP = this.transformedPoint(ev.x, ev.y)
|
|
|
const drag = (ev) => {
|
|
|
- const point = this.transformedPoint(ev.clientX, ev.clientY)
|
|
|
- tnode.x = point.x - delta.x
|
|
|
- tnode.y = point.y - delta.y
|
|
|
+ this.dragging = tnode
|
|
|
+ const dragP = this.transformedPoint(ev.x, ev.y)
|
|
|
+ for (let n of nodeSel) {
|
|
|
+ n.x += dragP.x - curP.x
|
|
|
+ n.y += dragP.y - curP.y
|
|
|
+ }
|
|
|
+ this.sendFlowEvent('nodeUpdate', nodeSel)
|
|
|
+ curP = dragP
|
|
|
// Bad possibly
|
|
|
- this.$emit('input', this.nodeData)
|
|
|
- this.$emit('nodeUpdate', tnode)
|
|
|
+ // this.$emit('input', this.nodeData)
|
|
|
}
|
|
|
const drop = (ev) => {
|
|
|
document.removeEventListener('mousemove', drag)
|
|
|
document.removeEventListener('mouseup', drop)
|
|
|
+ if (!this.dragging) {
|
|
|
+ if (!ev.ctrlKey) this.nodeSelection = {}
|
|
|
+ this.nodeSelection[tnode.id] = tnode
|
|
|
+ }
|
|
|
this.dragging = null
|
|
|
|
|
|
- this.$emit('input', this.nodeData)
|
|
|
- this.$emit('nodeUpdate', tnode)
|
|
|
- this.$emit('shouldPersist')
|
|
|
+ this.sendFlowEvent('nodeUpdate', nodeSel)
|
|
|
+ this.sendDocumentUpdate()
|
|
|
}
|
|
|
document.addEventListener('mousemove', drag)
|
|
|
document.addEventListener('mouseup', drop)
|
|
@@ -341,24 +405,21 @@ export default {
|
|
|
src: src
|
|
|
}
|
|
|
this.nodeData.nodes.push(newNode)
|
|
|
- this.$emit('input', this.nodeData)
|
|
|
- this.$emit('nodeUpdate', newNode)
|
|
|
- this.$emit('shouldPersist')
|
|
|
+ this.sendFlowEvent('nodeUpdate', [newNode])
|
|
|
+ this.sendDocumentUpdate()
|
|
|
},
|
|
|
linkAdd (link) {
|
|
|
this.nodeData.links.push(link)
|
|
|
- this.$emit('input', this.nodeData)
|
|
|
- this.$emit('linkUpdate', link)
|
|
|
- this.$emit('shouldPersist')
|
|
|
+ this.sendflowEvent('linkUpdate', link)
|
|
|
+ this.sendDocumentUpdate()
|
|
|
},
|
|
|
linkRemove (link) {
|
|
|
const i = this.nodeData.links.findIndex(l => l === link)
|
|
|
if (i === -1) return
|
|
|
|
|
|
this.nodeData.links.splice(i, 1)
|
|
|
- this.$emit('input', this.nodeData)
|
|
|
- this.$emit('linkRemove', link)
|
|
|
- this.$emit('shouldPersist')
|
|
|
+ this.sendFlowEvent('linkRemove', link)
|
|
|
+ this.sendDocumentUpdate()
|
|
|
},
|
|
|
managerDrop (ev) {
|
|
|
ev.preventDefault()
|
|
@@ -371,6 +432,48 @@ export default {
|
|
|
const pt = this.transformedPoint(ev.x, ev.y)
|
|
|
this.nodeAdd(reg, pt.x, pt.y)
|
|
|
},
|
|
|
+ viewPointerDown (ev) {
|
|
|
+ console.log('Starting selector')
|
|
|
+ ev.preventDefault()
|
|
|
+ this.nodeSelection = {}
|
|
|
+ const px = ev.x
|
|
|
+ const py = ev.y
|
|
|
+
|
|
|
+ const p = this.transformedPoint(px, py)
|
|
|
+ this.selector = {x: p.x, y: p.y, width: 0, height: 0}
|
|
|
+ const drag = (ev) => {
|
|
|
+ const p = this.transformedPoint(px, py)
|
|
|
+ const p2 = this.transformedPoint(ev.x, ev.y)
|
|
|
+ const nwidth = p2.x - p.x
|
|
|
+ const nheight = p2.y - p.y
|
|
|
+ this.selector.x = p.x
|
|
|
+ this.selector.y = p.y
|
|
|
+ this.selector.width = nwidth
|
|
|
+ this.selector.height = nheight
|
|
|
+ if (nwidth < 0) {
|
|
|
+ this.selector.x = p2.x
|
|
|
+ this.selector.width = -nwidth
|
|
|
+ }
|
|
|
+ if (nheight < 0) {
|
|
|
+ this.selector.y = p2.y
|
|
|
+ this.selector.height = -nheight
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const drop = (ev) => {
|
|
|
+ this.selector = null
|
|
|
+ document.removeEventListener('mousemove', drag)
|
|
|
+ document.removeEventListener('mouseup', drop)
|
|
|
+ }
|
|
|
+ document.addEventListener('mousemove', drag)
|
|
|
+ document.addEventListener('mouseup', drop)
|
|
|
+ },
|
|
|
+ sendFlowEvent (type, param) {
|
|
|
+ this.$flowService[type](param)
|
|
|
+ },
|
|
|
+
|
|
|
+ sendDocumentUpdate (nodeData) {
|
|
|
+ this.$flowService.documentUpdate(this.nodeData, this.$route.params.sessId)
|
|
|
+ },
|
|
|
// HELPERS depending on svg ref
|
|
|
createSVGPoint (x, y) {
|
|
|
const p = this.$refs.svg.createSVGPoint()
|
|
@@ -434,4 +537,16 @@ function guid () {
|
|
|
fill:transparent;
|
|
|
}
|
|
|
|
|
|
+.flow-selector {
|
|
|
+ pointer-events:none;
|
|
|
+ opacity:0;
|
|
|
+ /* TODO: theme */
|
|
|
+ stroke: rgba(30,100,255,1);
|
|
|
+ fill: rgba(30,100,255,0.3);
|
|
|
+}
|
|
|
+
|
|
|
+.flow-selector.flow-selector--selecting {
|
|
|
+ opacity:1;
|
|
|
+}
|
|
|
+
|
|
|
</style>
|