|
@@ -9,19 +9,32 @@
|
|
{{ k }}
|
|
{{ k }}
|
|
</button>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
+
|
|
<svg
|
|
<svg
|
|
|
|
+ xmlns="http://www.w3.org/2000/svg"
|
|
|
|
+ xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
ref="svg"
|
|
ref="svg"
|
|
class="view"
|
|
class="view"
|
|
:width="width"
|
|
:width="width"
|
|
:height="height">
|
|
:height="height">
|
|
|
|
+ <defs>
|
|
|
|
+ <pattern id="smallGrid" width="8" height="8" pattern-units="userSpaceOnUse">
|
|
|
|
+ <path d="M 8 0 L 0 0 0 8" fill="none" stroke="gray" stroke-width="0.5"/>
|
|
|
|
+ </pattern>
|
|
|
|
+ <pattern id="grid" width="80" height="80" pattern-units="userSpaceOnUse">
|
|
|
|
+ <rect width="80" height="80" fill="url(#smallGrid)"/>
|
|
|
|
+ <path d="M 80 0 L 0 0 0 80" fill="none" stroke="gray" stroke-width="1"/>
|
|
|
|
+ </pattern>
|
|
|
|
+ </defs>
|
|
|
|
+
|
|
<flow-pan-zoom
|
|
<flow-pan-zoom
|
|
ref="panzoom"
|
|
ref="panzoom"
|
|
v-model="panzoom">
|
|
v-model="panzoom">
|
|
<flow-link
|
|
<flow-link
|
|
- v-for="(l,i) in nodeData.links"
|
|
|
|
|
|
+ v-for="(link,i) in nodeData.links"
|
|
:key="i"
|
|
:key="i"
|
|
- v-bind="linkProps(l)"
|
|
|
|
- @click="linkRemove(i)"
|
|
|
|
|
|
+ v-bind="linkProps(link)"
|
|
|
|
+ @click="linkRemove(link)"
|
|
/>
|
|
/>
|
|
<!-- mouse link-->
|
|
<!-- mouse link-->
|
|
<flow-link
|
|
<flow-link
|
|
@@ -57,10 +70,11 @@ export default {
|
|
'height': {type: String, default: '600px'}
|
|
'height': {type: String, default: '600px'}
|
|
},
|
|
},
|
|
data () {
|
|
data () {
|
|
- const cloned = JSON.parse(JSON.stringify(this.value)) // initial?
|
|
|
|
|
|
+ // const cloned = JSON.parse(JSON.stringify(this.value)) // initial?
|
|
return {
|
|
return {
|
|
panzoom: { x: 0, y: 0, zoom: 1 },
|
|
panzoom: { x: 0, y: 0, zoom: 1 },
|
|
- nodeData: cloned,
|
|
|
|
|
|
+ nodeData: { nodes: [], links: [] },
|
|
|
|
+ dragging: null,
|
|
pointerLink: {active: false, props: {}, src: {}}
|
|
pointerLink: {active: false, props: {}, src: {}}
|
|
}
|
|
}
|
|
},
|
|
},
|
|
@@ -115,7 +129,21 @@ export default {
|
|
watch: {
|
|
watch: {
|
|
value: {
|
|
value: {
|
|
handler (val) {
|
|
handler (val) {
|
|
- this.nodeData = JSON.parse(JSON.stringify(this.value)) // deepClone
|
|
|
|
|
|
+ const clone = JSON.parse(JSON.stringify(this.value)) // deepClone
|
|
|
|
+ const nodes = []
|
|
|
|
+ for (let n of clone.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: clone.links
|
|
|
|
+ }
|
|
this.$nextTick(() => {
|
|
this.$nextTick(() => {
|
|
this.$forceUpdate()
|
|
this.$forceUpdate()
|
|
})
|
|
})
|
|
@@ -130,6 +158,7 @@ export default {
|
|
},
|
|
},
|
|
methods: {
|
|
methods: {
|
|
// XXX: Shrink this function
|
|
// XXX: Shrink this function
|
|
|
|
+ // and create some LinkAdd method
|
|
socketPointerDown (nodeId, e, socket) {
|
|
socketPointerDown (nodeId, e, socket) {
|
|
const nodeRef = this.$refs.nodes.find(n => n.id === nodeId)
|
|
const nodeRef = this.$refs.nodes.find(n => n.id === nodeId)
|
|
const node = this.nodeData.nodes.find(n => n.id === nodeId)
|
|
const node = this.nodeData.nodes.find(n => n.id === nodeId)
|
|
@@ -222,9 +251,7 @@ export default {
|
|
// console.error('LINK: already has input')
|
|
// console.error('LINK: already has input')
|
|
// return
|
|
// return
|
|
}
|
|
}
|
|
-
|
|
|
|
- this.nodeData.links.push(link)
|
|
|
|
- this.$emit('input', this.nodeData)
|
|
|
|
|
|
+ this.linkAdd(link)
|
|
}
|
|
}
|
|
document.addEventListener('mousemove', drag)
|
|
document.addEventListener('mousemove', drag)
|
|
document.addEventListener('mouseup', drop)
|
|
document.addEventListener('mouseup', drop)
|
|
@@ -237,6 +264,8 @@ export default {
|
|
this.nodeData.links = this.nodeData.links.filter(l => l.from !== tnode.id && l.to !== tnode.id)
|
|
this.nodeData.links = this.nodeData.links.filter(l => l.from !== tnode.id && l.to !== tnode.id)
|
|
this.nodeData.nodes.splice(i, 1)
|
|
this.nodeData.nodes.splice(i, 1)
|
|
this.$emit('input', this.nodeData)
|
|
this.$emit('input', this.nodeData)
|
|
|
|
+ this.$emit('nodeRemove', tnode)
|
|
|
|
+ this.$emit('shouldPersist')
|
|
return
|
|
return
|
|
}
|
|
}
|
|
if (e.button !== 0) return // first button
|
|
if (e.button !== 0) return // first button
|
|
@@ -244,6 +273,7 @@ export default {
|
|
this.nodeData.nodes.splice(i, 1)
|
|
this.nodeData.nodes.splice(i, 1)
|
|
this.nodeData.nodes.push(tnode) // put in last
|
|
this.nodeData.nodes.push(tnode) // put in last
|
|
|
|
|
|
|
|
+ this.dragging = tnode
|
|
// transform CTM
|
|
// transform CTM
|
|
const delta = this.transformedPoint(e.clientX, e.clientY)
|
|
const delta = this.transformedPoint(e.clientX, e.clientY)
|
|
delta.x -= tnode.x
|
|
delta.x -= tnode.x
|
|
@@ -255,30 +285,50 @@ export default {
|
|
tnode.y = point.y - delta.y
|
|
tnode.y = point.y - delta.y
|
|
// Bad possibly
|
|
// Bad possibly
|
|
this.$emit('input', this.nodeData)
|
|
this.$emit('input', this.nodeData)
|
|
|
|
+ this.$emit('nodeUpdate', tnode)
|
|
}
|
|
}
|
|
const drop = (e) => {
|
|
const drop = (e) => {
|
|
document.removeEventListener('mousemove', drag)
|
|
document.removeEventListener('mousemove', drag)
|
|
document.removeEventListener('mouseup', drop)
|
|
document.removeEventListener('mouseup', drop)
|
|
|
|
+ this.dragging = null
|
|
|
|
+
|
|
this.$emit('input', this.nodeData)
|
|
this.$emit('input', this.nodeData)
|
|
|
|
+ this.$emit('nodeUpdate', tnode)
|
|
|
|
+ this.$emit('shouldPersist')
|
|
}
|
|
}
|
|
document.addEventListener('mousemove', drag)
|
|
document.addEventListener('mousemove', drag)
|
|
document.addEventListener('mouseup', drop)
|
|
document.addEventListener('mouseup', drop)
|
|
},
|
|
},
|
|
nodeAdd (k) {
|
|
nodeAdd (k) {
|
|
- this.nodeData.nodes.push({
|
|
|
|
|
|
+ const newNode = {
|
|
id: guid(),
|
|
id: guid(),
|
|
x: 100,
|
|
x: 100,
|
|
y: 100,
|
|
y: 100,
|
|
label: k,
|
|
label: k,
|
|
src: k
|
|
src: k
|
|
- })
|
|
|
|
|
|
+ }
|
|
|
|
+ this.nodeData.nodes.push(newNode)
|
|
this.$emit('input', this.nodeData)
|
|
this.$emit('input', this.nodeData)
|
|
|
|
+ this.$emit('nodeUpdate', newNode)
|
|
|
|
+ this.$emit('shouldPersist')
|
|
},
|
|
},
|
|
- linkRemove (i) {
|
|
|
|
|
|
+ linkAdd (link) {
|
|
|
|
+ this.nodeData.links.push(link)
|
|
|
|
+ this.$emit('input', this.nodeData)
|
|
|
|
+ this.$emit('linkUpdate', link)
|
|
|
|
+ this.$emit('shouldPersist')
|
|
|
|
+ },
|
|
|
|
+ linkRemove (link) {
|
|
|
|
+ const i = this.nodeData.links.findIndex(l => l === link)
|
|
|
|
+ if (i === -1) return
|
|
|
|
+
|
|
this.nodeData.links.splice(i, 1)
|
|
this.nodeData.links.splice(i, 1)
|
|
this.$emit('input', this.nodeData)
|
|
this.$emit('input', this.nodeData)
|
|
|
|
+ this.$emit('linkRemove', link)
|
|
|
|
+ this.$emit('shouldPersist')
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+ // HELPERS depending on svg ref
|
|
createSVGPoint (x, y) {
|
|
createSVGPoint (x, y) {
|
|
const p = this.$refs.svg.createSVGPoint()
|
|
const p = this.$refs.svg.createSVGPoint()
|
|
p.x = x; p.y = y
|
|
p.x = x; p.y = y
|
|
@@ -287,14 +337,12 @@ export default {
|
|
transformedPoint (x, y, abs) {
|
|
transformedPoint (x, y, abs) {
|
|
const svgRect = this.$refs.svg.getBoundingClientRect()
|
|
const svgRect = this.$refs.svg.getBoundingClientRect()
|
|
if (!abs) {
|
|
if (!abs) {
|
|
- x -= svgRect.x
|
|
|
|
- y -= svgRect.y
|
|
|
|
|
|
+ x -= svgRect.left
|
|
|
|
+ y -= svgRect.top
|
|
}
|
|
}
|
|
return this.$refs.panzoom.transformedPoint(this.createSVGPoint(x, y))
|
|
return this.$refs.panzoom.transformedPoint(this.createSVGPoint(x, y))
|
|
}
|
|
}
|
|
- // helper
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
}
|
|
}
|
|
function guid () {
|
|
function guid () {
|
|
function s4 () {
|
|
function s4 () {
|