@live-change/flow-frontend 0.3.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/front/index.html +11 -0
- package/front/public/favicon.ico +0 -0
- package/front/public/images/empty-photo.svg +38 -0
- package/front/public/images/empty-user-photo.svg +33 -0
- package/front/public/images/logo.svg +34 -0
- package/front/public/images/logo128.png +0 -0
- package/front/src/App.vue +40 -0
- package/front/src/components/Edge.vue +63 -0
- package/front/src/components/EdgeBezierCurve.vue +100 -0
- package/front/src/components/EdgeEndHandle.vue +130 -0
- package/front/src/components/Flow.vue +198 -0
- package/front/src/components/Node.vue +36 -0
- package/front/src/components/NodeHandle.vue +37 -0
- package/front/src/components/NodePort.vue +71 -0
- package/front/src/components/index.js +417 -0
- package/front/src/entry-client.js +6 -0
- package/front/src/entry-server.js +6 -0
- package/front/src/router.js +37 -0
- package/front/vite.config.js +16 -0
- package/index.js +11 -0
- package/package.json +68 -0
- package/server/init.js +8 -0
- package/server/services.config.js +26 -0
package/front/index.html
ADDED
|
Binary file
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
3
|
+
|
|
4
|
+
<svg
|
|
5
|
+
version="1.1"
|
|
6
|
+
id="Layer_1"
|
|
7
|
+
x="0px"
|
|
8
|
+
y="0px"
|
|
9
|
+
width="512px"
|
|
10
|
+
height="512px"
|
|
11
|
+
viewBox="0 0 512 512"
|
|
12
|
+
enable-background="new 0 0 512 512"
|
|
13
|
+
xml:space="preserve"
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
xmlns:svg="http://www.w3.org/2000/svg"
|
|
16
|
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
17
|
+
xmlns:cc="http://creativecommons.org/ns#"
|
|
18
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata
|
|
19
|
+
id="metadata9"><rdf:RDF><cc:Work
|
|
20
|
+
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
|
21
|
+
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
|
22
|
+
id="defs7" />
|
|
23
|
+
<rect
|
|
24
|
+
style="fill:#dbdbdb;fill-opacity:1;stroke:none;stroke-width:1.88976383;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
25
|
+
id="rect821"
|
|
26
|
+
width="512.54236"
|
|
27
|
+
height="512.54236"
|
|
28
|
+
x="0.54237288"
|
|
29
|
+
y="0.54237235" />
|
|
30
|
+
<path
|
|
31
|
+
style="fill:#f6f6f6;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
32
|
+
d="M 0.54237288,470.23729 125.83051,306.44068 l 60.20339,70.50847 130.71186,-174.10169 195.79661,261.42373 0.54235,48.81353 H 0.54237288 Z"
|
|
33
|
+
id="path816" /><circle
|
|
34
|
+
style="fill:#f6f6f6;fill-opacity:1;stroke:none;stroke-width:4.80000019;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
35
|
+
id="path818"
|
|
36
|
+
cx="118.50848"
|
|
37
|
+
cy="106.57627"
|
|
38
|
+
r="57.762711" /></svg>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
3
|
+
|
|
4
|
+
<svg
|
|
5
|
+
version="1.1"
|
|
6
|
+
id="Layer_1"
|
|
7
|
+
x="0px"
|
|
8
|
+
y="0px"
|
|
9
|
+
width="512px"
|
|
10
|
+
height="512px"
|
|
11
|
+
viewBox="0 0 512 512"
|
|
12
|
+
enable-background="new 0 0 512 512"
|
|
13
|
+
xml:space="preserve"
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
xmlns:svg="http://www.w3.org/2000/svg"
|
|
16
|
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
17
|
+
xmlns:cc="http://creativecommons.org/ns#"
|
|
18
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata
|
|
19
|
+
id="metadata9"><rdf:RDF><cc:Work
|
|
20
|
+
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
|
21
|
+
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
|
22
|
+
id="defs7" />
|
|
23
|
+
<rect
|
|
24
|
+
style="fill:#dbdbdb;fill-opacity:1;stroke:none;stroke-width:1.88976383;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
25
|
+
id="rect821"
|
|
26
|
+
width="512.54236"
|
|
27
|
+
height="512.54236"
|
|
28
|
+
x="0.54237288"
|
|
29
|
+
y="0.54237235" /><path
|
|
30
|
+
d="m 313.14122,310.64877 c 9.02181,-8.85097 12.15686,-31.23733 20.41627,-39.3056 11.69771,-11.43183 24.39381,-25.50218 27.98588,-43.06851 4.82003,-23.34203 -4.96846,-26.30516 -4.96846,-34.3713 0,-16.90002 -0.15376,-44.65739 -5.43081,-62.68607 -0.30326,-23.7809 -4.20499,-34.123578 -12.38858,-43.031138 -7.72017,-8.33522 -26.68637,-6.366203 -36.59125,-11.85361 C 286.83605,67.825407 274.23818,64.594254 258.15823,64.17461 v -0.09503 c -0.47624,0 -0.93539,0.05659 -1.41162,0.05659 -0.78484,0 -1.53123,-0.05659 -2.35343,-0.05659 l 0.0395,0.151627 c -39.17105,1.472492 -80.84608,26.170627 -95.16524,65.895863 -5.31335,14.71958 -3.28881,46.87521 -3.28881,63.77522 0,8.06721 -9.78742,11.02927 -4.96846,34.37131 3.61129,17.56632 16.28817,31.63668 27.98588,43.06851 8.26048,8.06827 11.39446,30.45462 20.39705,39.30559 1.96902,13.0378 2.1986,9.31012 2.12278,21.23954 -0.0203,4.03307 0.11426,10.28502 -6.32669,16.4398 -12.17822,11.6614 -40.14595,36.95324 -63.08756,46.79832 -30.24,12.98014 -79.41203,33.74237 -90.746691,39.59069 -11.334661,5.84833 -41.2532541,21.69763 -41.2532541,31.79045 0,10.11417 0,47.33542 0,47.33542 h 253.3742451 5.79494 253.37318 c 0,0 0,-37.22125 0,-47.33542 0,-10.09282 -29.89723,-25.94212 -41.25645,-31.79045 -11.35389,-5.84938 -60.48321,-26.61055 -90.72641,-39.59069 -22.32976,-9.57707 -49.43685,-33.81712 -62.09344,-45.84158 -4.2808,-4.05228 -7.3016,-8.29784 -7.3411,-18.62023 -0.0737,-12.00417 0.23278,-7.41692 2.14306,-20.01478"
|
|
31
|
+
id="path2"
|
|
32
|
+
style="clip-rule:evenodd;fill-rule:evenodd;stroke-width:1.06779659;fill:#f6f6f6;fill-opacity:1" />
|
|
33
|
+
</svg>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg
|
|
3
|
+
width="512"
|
|
4
|
+
height="512"
|
|
5
|
+
viewBox="0 0 135.46667 135.46667"
|
|
6
|
+
version="1.1"
|
|
7
|
+
id="svg977"
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
xmlns:svg="http://www.w3.org/2000/svg"
|
|
10
|
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
11
|
+
xmlns:cc="http://creativecommons.org/ns#"
|
|
12
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
13
|
+
<defs
|
|
14
|
+
id="defs971" />
|
|
15
|
+
<metadata
|
|
16
|
+
id="metadata974">
|
|
17
|
+
<rdf:RDF>
|
|
18
|
+
<cc:Work
|
|
19
|
+
rdf:about="">
|
|
20
|
+
<dc:format>image/svg+xml</dc:format>
|
|
21
|
+
<dc:type
|
|
22
|
+
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
23
|
+
</cc:Work>
|
|
24
|
+
</rdf:RDF>
|
|
25
|
+
</metadata>
|
|
26
|
+
<g
|
|
27
|
+
id="layer1"
|
|
28
|
+
style="fill:#666666">
|
|
29
|
+
<path
|
|
30
|
+
style="fill:#666666;fill-opacity:1;stroke:none;stroke-width:2.70907px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
31
|
+
d="M 54.181439,13.558436 67.726801,0.01304296 135.45359,67.739804 67.726801,135.46667 29.799793,97.539626 V 65.030766 L 13.545362,81.285197 0,67.739804 40.636077,27.103727 81.272154,67.739804 67.726801,81.285197 51.472371,65.030766 V 92.12155 l 16.25443,16.25433 40.636069,-40.636076 z"
|
|
32
|
+
id="path892" />
|
|
33
|
+
</g>
|
|
34
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view-root>
|
|
3
|
+
<template #navbar>
|
|
4
|
+
<NavBar />
|
|
5
|
+
</template>
|
|
6
|
+
</view-root>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script setup>
|
|
10
|
+
import 'primevue/resources/themes/saga-green/theme.css'
|
|
11
|
+
import "@fortawesome/fontawesome-free/css/all.min.css"
|
|
12
|
+
|
|
13
|
+
import { ViewRoot, NavBar } from "@live-change/frontend-base"
|
|
14
|
+
|
|
15
|
+
import { computed } from 'vue'
|
|
16
|
+
import { useHead } from '@vueuse/head'
|
|
17
|
+
useHead(computed(() => ({
|
|
18
|
+
title: 'Title',
|
|
19
|
+
meta: [
|
|
20
|
+
{ charset: 'utf-8' },
|
|
21
|
+
{ name: 'viewport',
|
|
22
|
+
content: "user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1," +
|
|
23
|
+
" width=device-width, viewport-fit=cover" }
|
|
24
|
+
],
|
|
25
|
+
htmlAttrs: {
|
|
26
|
+
lang: 'en',
|
|
27
|
+
amp: true
|
|
28
|
+
}
|
|
29
|
+
})))
|
|
30
|
+
|
|
31
|
+
import { watch } from 'vue'
|
|
32
|
+
import { client as useClient, useApi } from '@live-change/vue3-ssr'
|
|
33
|
+
const client = useClient()
|
|
34
|
+
watch(client, (newClient, oldClient) => {
|
|
35
|
+
console.log("WATCH CLIENT", oldClient, '=>', newClient)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const api = useApi()
|
|
39
|
+
|
|
40
|
+
</script>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<g>
|
|
3
|
+
<slot v-if="src && dest" v-bind="{ src, dest }"></slot>
|
|
4
|
+
</g>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup>
|
|
8
|
+
|
|
9
|
+
import { computed, ref, defineProps, defineEmits, toRefs, inject } from "vue"
|
|
10
|
+
|
|
11
|
+
const props = defineProps({
|
|
12
|
+
edge: {
|
|
13
|
+
type: Object,
|
|
14
|
+
required: true
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const { edge } = toRefs(props)
|
|
19
|
+
|
|
20
|
+
const flow = inject("flow")
|
|
21
|
+
|
|
22
|
+
const srcView = computed(() => edge.value.src.node && flow.getPortView(edge.value.src.node, edge.value.src.port))
|
|
23
|
+
const destView = computed(() => edge.value.dest.node && flow.getPortView(edge.value.dest.node, edge.value.dest.port))
|
|
24
|
+
|
|
25
|
+
function edgePoint(edgePort, view, otherEdgePort) {
|
|
26
|
+
if(view) {
|
|
27
|
+
return {
|
|
28
|
+
...edgePort,
|
|
29
|
+
x: view.rect.x + view.rect.width / 2,
|
|
30
|
+
y: view.rect.y + view.rect.height / 2,
|
|
31
|
+
direction: view.direction,
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if(edgePort.x && edgePort.y) {
|
|
35
|
+
const searchPosition = { x: edgePort.x, y: edgePort.y }
|
|
36
|
+
const maxDistance = flow.options.edgeSnapDistance ?? 23
|
|
37
|
+
const { portView } = flow.getNearestPortView(searchPosition, maxDistance,
|
|
38
|
+
(node, port) => flow.options.isConnectable
|
|
39
|
+
? flow.options.isConnectable(otherEdgePort.node, otherEdgePort.port, node, port) : true
|
|
40
|
+
)
|
|
41
|
+
//console.log('PV', flow.options, portView)
|
|
42
|
+
if(portView) {
|
|
43
|
+
return {
|
|
44
|
+
...edgePort,
|
|
45
|
+
x: portView.rect.x + portView.rect.width / 2,
|
|
46
|
+
y: portView.rect.y + portView.rect.height / 2,
|
|
47
|
+
direction: portView.direction,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
...edgePort
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const src = computed(() => edgePoint(edge.value.src, srcView.value, edge.value.dest))
|
|
57
|
+
const dest = computed(() => edgePoint(edge.value.dest, destView.value, edge.value.src))
|
|
58
|
+
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<style scoped>
|
|
62
|
+
|
|
63
|
+
</style>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<slot v-bind="{ bezierPath }">
|
|
3
|
+
<path :d="bezierPath" stroke="black" fill="none" stroke-width="10" stroke-linecap="round" />
|
|
4
|
+
</slot>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup>
|
|
8
|
+
|
|
9
|
+
import { defineProps, defineEmits, toRefs, computed } from "vue"
|
|
10
|
+
|
|
11
|
+
const props = defineProps({
|
|
12
|
+
src: {
|
|
13
|
+
type: Object,
|
|
14
|
+
required: true,
|
|
15
|
+
properties: {
|
|
16
|
+
x: {
|
|
17
|
+
type: Number,
|
|
18
|
+
required: true
|
|
19
|
+
},
|
|
20
|
+
y: {
|
|
21
|
+
type: String,
|
|
22
|
+
required: true
|
|
23
|
+
},
|
|
24
|
+
direction: {
|
|
25
|
+
type: Object,
|
|
26
|
+
required: false,
|
|
27
|
+
default: null,
|
|
28
|
+
properties: {
|
|
29
|
+
x: {
|
|
30
|
+
type: Number,
|
|
31
|
+
required: true
|
|
32
|
+
},
|
|
33
|
+
y: {
|
|
34
|
+
type: Number,
|
|
35
|
+
required: true
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
dest: {
|
|
42
|
+
type: Object,
|
|
43
|
+
required: true,
|
|
44
|
+
properties: {
|
|
45
|
+
x: {
|
|
46
|
+
type: Number,
|
|
47
|
+
required: true
|
|
48
|
+
},
|
|
49
|
+
y: {
|
|
50
|
+
type: String,
|
|
51
|
+
required: true
|
|
52
|
+
},
|
|
53
|
+
direction: {
|
|
54
|
+
type: Object,
|
|
55
|
+
required: false,
|
|
56
|
+
default: null,
|
|
57
|
+
properties: {
|
|
58
|
+
x: {
|
|
59
|
+
type: Number,
|
|
60
|
+
required: true
|
|
61
|
+
},
|
|
62
|
+
y: {
|
|
63
|
+
type: Number,
|
|
64
|
+
required: true
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
endpointLength: {
|
|
71
|
+
type: Number,
|
|
72
|
+
required: false,
|
|
73
|
+
default: 90
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
const { src, dest, endpointLength } = toRefs(props)
|
|
78
|
+
|
|
79
|
+
const bezierPath = computed(() => {
|
|
80
|
+
const f = src.value
|
|
81
|
+
const t = dest.value
|
|
82
|
+
const el = endpointLength.value
|
|
83
|
+
const dx = t.x - f.x
|
|
84
|
+
const dy = t.y - f.y
|
|
85
|
+
const fdx = f.direction?.x ?? 0
|
|
86
|
+
const fdy = f.direction?.y ?? 0
|
|
87
|
+
const tdx = t.direction?.x ?? 0
|
|
88
|
+
const tdy = t.direction?.y ?? 0
|
|
89
|
+
return [
|
|
90
|
+
`M ${f.x} ${f.y}`,
|
|
91
|
+
`c ${fdx * el} ${fdy * el}, ${dx + tdx * el} ${dy + tdy * el}, ${dx} ${dy}`
|
|
92
|
+
//`l ${dx} ${dy}`,
|
|
93
|
+
].join(' ')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
</script>
|
|
97
|
+
|
|
98
|
+
<style scoped>
|
|
99
|
+
|
|
100
|
+
</style>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<slot v-bind="{ end, linePath }">
|
|
3
|
+
<path @mousedown="handleMouseDown"
|
|
4
|
+
@touchstart="handleTouchStart"
|
|
5
|
+
:d="linePath"
|
|
6
|
+
class="pointer-events-auto" style="cursor: move"
|
|
7
|
+
stroke="rgba(0,0,0,0)" fill="none" stroke-width="19" stroke-linecap="round" />
|
|
8
|
+
</slot>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
|
|
13
|
+
import { defineProps, defineEmits, toRefs, computed, inject } from "vue"
|
|
14
|
+
|
|
15
|
+
const props = defineProps({
|
|
16
|
+
position: {
|
|
17
|
+
type: Object,
|
|
18
|
+
required: true,
|
|
19
|
+
properties: {
|
|
20
|
+
x: {
|
|
21
|
+
type: Number,
|
|
22
|
+
required: true
|
|
23
|
+
},
|
|
24
|
+
y: {
|
|
25
|
+
type: String,
|
|
26
|
+
required: true
|
|
27
|
+
},
|
|
28
|
+
direction: {
|
|
29
|
+
type: Object,
|
|
30
|
+
required: false,
|
|
31
|
+
default: null,
|
|
32
|
+
properties: {
|
|
33
|
+
x: {
|
|
34
|
+
type: Number,
|
|
35
|
+
required: true
|
|
36
|
+
},
|
|
37
|
+
y: {
|
|
38
|
+
type: Number,
|
|
39
|
+
required: true
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
edge: {
|
|
46
|
+
type: Object,
|
|
47
|
+
required: true,
|
|
48
|
+
properties: {
|
|
49
|
+
type: {
|
|
50
|
+
type: String,
|
|
51
|
+
default: undefined
|
|
52
|
+
},
|
|
53
|
+
src: {
|
|
54
|
+
type: Object,
|
|
55
|
+
required: true,
|
|
56
|
+
properties: {
|
|
57
|
+
node: {
|
|
58
|
+
type: String,
|
|
59
|
+
required: true
|
|
60
|
+
},
|
|
61
|
+
port: {
|
|
62
|
+
type: String,
|
|
63
|
+
required: true
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
dest: {
|
|
68
|
+
type: Object,
|
|
69
|
+
required: true,
|
|
70
|
+
properties: {
|
|
71
|
+
node: {
|
|
72
|
+
type: String,
|
|
73
|
+
required: true
|
|
74
|
+
},
|
|
75
|
+
port: {
|
|
76
|
+
type: String,
|
|
77
|
+
required: true
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
end: {
|
|
84
|
+
type: String,
|
|
85
|
+
required: true
|
|
86
|
+
},
|
|
87
|
+
endLength: {
|
|
88
|
+
type: Number,
|
|
89
|
+
default: 10
|
|
90
|
+
},
|
|
91
|
+
endDistance: {
|
|
92
|
+
type: Number,
|
|
93
|
+
default: 15
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
const { position, edge, end, endLength, endDistance } = toRefs(props)
|
|
98
|
+
|
|
99
|
+
const linePath = computed(() => {
|
|
100
|
+
const p = position.value
|
|
101
|
+
const el = endLength.value
|
|
102
|
+
const ed = endDistance.value
|
|
103
|
+
return [
|
|
104
|
+
...(p.direction ? [
|
|
105
|
+
`M ${p.x + p.direction.x * ed} ${p.y + p.direction.y * ed}`,
|
|
106
|
+
`l ${p.direction.x * el} ${p.direction.y * el}`
|
|
107
|
+
] : [
|
|
108
|
+
`M ${p.x} ${p.y}`,
|
|
109
|
+
]),
|
|
110
|
+
`l 0 0`,
|
|
111
|
+
].join(' ')
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
const flow = inject("flow")
|
|
115
|
+
|
|
116
|
+
function handleMouseDown(event) {
|
|
117
|
+
event.preventDefault()
|
|
118
|
+
flow.startDragEdgeEnd(edge.value, end.value, event)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function handleTouchStart(event) {
|
|
122
|
+
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
</script>
|
|
127
|
+
|
|
128
|
+
<style scoped>
|
|
129
|
+
|
|
130
|
+
</style>
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div ref="viewport" class="overflow-hidden bg-gray-300"
|
|
3
|
+
@mousedown.capture="handleMouseDown"
|
|
4
|
+
@wheel.capture="handleWheel"
|
|
5
|
+
@mousemove="handleMouseMove"
|
|
6
|
+
@mouseup="handleMouseUp"
|
|
7
|
+
@mouseleave="handleMouseLeave">
|
|
8
|
+
<!-- <pre class="absolute text-xs">{{ JSON.stringify({
|
|
9
|
+
...dragState,
|
|
10
|
+
node: undefined
|
|
11
|
+
}, null, 2) }}</pre>-->
|
|
12
|
+
<div ref="container"
|
|
13
|
+
class="bg-white"
|
|
14
|
+
:style="{
|
|
15
|
+
transform, transformOrigin: '0 0',
|
|
16
|
+
width: flow?.size?.width + 'px', height: flow?.size?.height + 'px'
|
|
17
|
+
}">
|
|
18
|
+
<!-- <pre class="absolute text-xs">{{ JSON.stringify(freeEdge, null, 2) }}</pre>-->
|
|
19
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="absolute w-full h-full pointer-events-none">
|
|
20
|
+
<slot name="background-edges" v-bind="flow" />
|
|
21
|
+
</svg>
|
|
22
|
+
<slot v-bind="flow" />
|
|
23
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="absolute w-full h-full pointer-events-none">
|
|
24
|
+
<slot name="foreground-edges" v-bind="flow" />
|
|
25
|
+
<slot name="free-edge" v-bind="{ freeEdge, flow }" />
|
|
26
|
+
</svg>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup>
|
|
32
|
+
|
|
33
|
+
import { defineProps, defineEmits, toRefs, reactive, ref, onMounted, onUnmounted, watch, computed } from "vue"
|
|
34
|
+
import { useFlow } from "./index.js"
|
|
35
|
+
|
|
36
|
+
const viewport = ref(null)
|
|
37
|
+
const container = ref(null)
|
|
38
|
+
|
|
39
|
+
const flow = useFlow()
|
|
40
|
+
|
|
41
|
+
watch(() => container.value, (container) => {
|
|
42
|
+
flow.container.value = container
|
|
43
|
+
}, { immediate: true })
|
|
44
|
+
onUnmounted(() => {
|
|
45
|
+
flow.container.value = null
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
watch(() => viewport.value, (viewport) => {
|
|
49
|
+
flow.viewport.value = viewport
|
|
50
|
+
}, { immediate: true })
|
|
51
|
+
onUnmounted(() => {
|
|
52
|
+
flow.viewport.value = null
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const transform = computed(() => {
|
|
56
|
+
return `translate(${flow.position.x}px, ${flow.position.y}px) scale(${flow.scale.value})`
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const dragState = flow.dragState
|
|
60
|
+
|
|
61
|
+
const freeEdge = computed(() => {
|
|
62
|
+
if(!dragState.value) return null
|
|
63
|
+
if(dragState.value.type == "drawEdge") {
|
|
64
|
+
return {
|
|
65
|
+
src: {
|
|
66
|
+
node: dragState.value.node,
|
|
67
|
+
port: dragState.value.port
|
|
68
|
+
},
|
|
69
|
+
dest: {
|
|
70
|
+
x: mousePosition.x,
|
|
71
|
+
y: mousePosition.y
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
const mousePosition = reactive({ x: 0, y: 0 })
|
|
79
|
+
|
|
80
|
+
const dragHandlers = {
|
|
81
|
+
dragNode(event, position) {
|
|
82
|
+
const { node, initialPosition, startPosition } = dragState.value
|
|
83
|
+
const dx = position.x - startPosition.x
|
|
84
|
+
const dy = position.y - startPosition.y
|
|
85
|
+
node.position.x = initialPosition.x + dx
|
|
86
|
+
node.position.y = initialPosition.y + dy
|
|
87
|
+
flow.invalidateNodeView(node)
|
|
88
|
+
setTimeout(() => {
|
|
89
|
+
flow.invalidateNodeView(node)
|
|
90
|
+
}, 0)
|
|
91
|
+
},
|
|
92
|
+
pan(event, position) {
|
|
93
|
+
const { initialPosition, startPosition } = dragState.value
|
|
94
|
+
const dx = event.clientX - startPosition.x
|
|
95
|
+
const dy = event.clientY - startPosition.y
|
|
96
|
+
flow.updatePositionAndScale(
|
|
97
|
+
initialPosition.x + dx,
|
|
98
|
+
initialPosition.y + dy,
|
|
99
|
+
flow.scale.value
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const dragEndHandlers = {
|
|
105
|
+
dragNode(event) {
|
|
106
|
+
},
|
|
107
|
+
drawEdge(event) {
|
|
108
|
+
const searchPosition = { x: mousePosition.x, y: mousePosition.y }
|
|
109
|
+
const maxDistance = flow.options.edgeConnectDistance ?? 23
|
|
110
|
+
const { nodeView, portView } = flow.getNearestPortView(searchPosition, maxDistance,
|
|
111
|
+
(node, port) => flow.options.isConnectable
|
|
112
|
+
? flow.options.isConnectable(dragState.value.node, dragState.value.port, node, port) : true
|
|
113
|
+
)
|
|
114
|
+
if(portView) {
|
|
115
|
+
const newEdge = {
|
|
116
|
+
id: flow.generateEdgeId(),
|
|
117
|
+
...dragState.value.edge,
|
|
118
|
+
src: {
|
|
119
|
+
node: dragState.value.node,
|
|
120
|
+
port: dragState.value.port
|
|
121
|
+
},
|
|
122
|
+
dest: {
|
|
123
|
+
node: nodeView.node,
|
|
124
|
+
port: portView.port
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if(flow.options.connect) {
|
|
128
|
+
flow.options.connect(newEdge)
|
|
129
|
+
} else {
|
|
130
|
+
flow.edges.push(newEdge)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function handleMouseDown(event) {
|
|
137
|
+
//console.log("handleMouseDown", event, event.target)
|
|
138
|
+
if(event.target == viewport.value || event.target == container.value) {
|
|
139
|
+
event.preventDefault()
|
|
140
|
+
event.stopPropagation()
|
|
141
|
+
dragState.value = {
|
|
142
|
+
type: "pan",
|
|
143
|
+
startPosition: {
|
|
144
|
+
x: event.clientX,
|
|
145
|
+
y: event.clientY
|
|
146
|
+
},
|
|
147
|
+
initialPosition: {
|
|
148
|
+
x: flow.position.x,
|
|
149
|
+
y: flow.position.y
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function handleWheel(event) {
|
|
156
|
+
if(event.target == viewport.value || event.target == container.value) {
|
|
157
|
+
event.preventDefault()
|
|
158
|
+
event.stopPropagation()
|
|
159
|
+
const delta = event.deltaY
|
|
160
|
+
if(delta > 0) {
|
|
161
|
+
flow.zoom(event, 0.8)
|
|
162
|
+
}
|
|
163
|
+
if(delta < 0) {
|
|
164
|
+
flow.zoom(event, 1.25)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function handleMouseMove(event) {
|
|
170
|
+
const position = flow.getEventPosition(event)
|
|
171
|
+
mousePosition.x = position.x
|
|
172
|
+
mousePosition.y = position.y
|
|
173
|
+
if(!dragState.value) return
|
|
174
|
+
const handler = dragHandlers[dragState.value.type]
|
|
175
|
+
if(handler) handler(event, mousePosition)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function handleMouseUp(event) {
|
|
179
|
+
if(!dragState.value) return
|
|
180
|
+
handleMouseMove(event)
|
|
181
|
+
const handler = dragEndHandlers[dragState.value.type]
|
|
182
|
+
if(handler) handler(event, mousePosition)
|
|
183
|
+
dragState.value = null
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function handleMouseLeave(event) {
|
|
187
|
+
if(!dragState.value) return
|
|
188
|
+
const handler = dragEndHandlers[dragState.value.type]
|
|
189
|
+
if(handler) handler(event, mousePosition)
|
|
190
|
+
dragState.value = null
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
</script>
|
|
195
|
+
|
|
196
|
+
<style scoped>
|
|
197
|
+
|
|
198
|
+
</style>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div ref="element">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup>
|
|
8
|
+
|
|
9
|
+
import { defineProps, defineEmits, toRefs, inject, watch, onUnmounted, ref } from "vue"
|
|
10
|
+
|
|
11
|
+
const props = defineProps({
|
|
12
|
+
node: {
|
|
13
|
+
type: Object,
|
|
14
|
+
required: true
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const { node } = toRefs(props)
|
|
19
|
+
|
|
20
|
+
const flow = inject("flow")
|
|
21
|
+
|
|
22
|
+
const element = ref(null)
|
|
23
|
+
|
|
24
|
+
watch(() => element.value, (element) => {
|
|
25
|
+
flow.setNodeElement(node.value, element)
|
|
26
|
+
}, { immediate: true })
|
|
27
|
+
|
|
28
|
+
onUnmounted(() => {
|
|
29
|
+
flow.setNodeElement(node.value, null)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<style scoped>
|
|
35
|
+
|
|
36
|
+
</style>
|