@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.
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <!--head-->
5
+ </head>
6
+ <body>
7
+ <div id="app"><!--app-html--></div>
8
+ <!--app-data-->
9
+ <script type="module" src="/src/entry-client.js"></script>
10
+ </body>
11
+ </html>
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>