@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
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div @mousedown="handleMouseDown"
|
|
3
|
+
@touchstart="handleTouchStart"
|
|
4
|
+
style="cursor: move">
|
|
5
|
+
<slot />
|
|
6
|
+
</div>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script setup>
|
|
10
|
+
|
|
11
|
+
import { defineProps, defineEmits, toRefs, inject } from "vue"
|
|
12
|
+
|
|
13
|
+
const props = defineProps({
|
|
14
|
+
node: {
|
|
15
|
+
type: Object,
|
|
16
|
+
required: true
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const { node } = toRefs(props)
|
|
21
|
+
|
|
22
|
+
const flow = inject("flow")
|
|
23
|
+
|
|
24
|
+
function handleMouseDown(event) {
|
|
25
|
+
event.preventDefault()
|
|
26
|
+
flow.startDragNode(node.value, event)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function handleTouchStart(event) {
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<style scoped>
|
|
36
|
+
|
|
37
|
+
</style>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div @mousedown="handleMouseDown"
|
|
3
|
+
@touchstart="handleTouchStart"
|
|
4
|
+
ref="element"
|
|
5
|
+
style="cursor: crosshair">
|
|
6
|
+
<slot />
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup>
|
|
11
|
+
|
|
12
|
+
import { defineProps, defineEmits, toRefs, inject, watch, onUnmounted, ref } from "vue"
|
|
13
|
+
|
|
14
|
+
const props = defineProps({
|
|
15
|
+
node: {
|
|
16
|
+
type: Object,
|
|
17
|
+
required: true
|
|
18
|
+
},
|
|
19
|
+
portId: {
|
|
20
|
+
type: String,
|
|
21
|
+
required: true
|
|
22
|
+
},
|
|
23
|
+
direction: {
|
|
24
|
+
type: Object,
|
|
25
|
+
default: () => ({ x: 0, y: 0 }),
|
|
26
|
+
properties: {
|
|
27
|
+
x: {
|
|
28
|
+
type: Number,
|
|
29
|
+
required: true
|
|
30
|
+
},
|
|
31
|
+
y: {
|
|
32
|
+
type: Number,
|
|
33
|
+
required: true
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
newEdgeOptions: {
|
|
38
|
+
type: Object,
|
|
39
|
+
default: () => ({})
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const { node, portId, direction, newEdgeOptions } = toRefs(props)
|
|
44
|
+
|
|
45
|
+
const flow = inject("flow")
|
|
46
|
+
|
|
47
|
+
const element = ref(null)
|
|
48
|
+
|
|
49
|
+
watch(() => element.value, (element) => {
|
|
50
|
+
flow.setPortElement(node.value, portId.value, element, props.direction)
|
|
51
|
+
}, { immediate: true })
|
|
52
|
+
|
|
53
|
+
onUnmounted(() => {
|
|
54
|
+
flow.setPortElement(node.value, portId.value, null)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
function handleMouseDown(event) {
|
|
58
|
+
event.preventDefault()
|
|
59
|
+
console.log("start draw edge", node.value.id, portId.value)
|
|
60
|
+
flow.startDrawEdge(node.value, portId.value, event, newEdgeOptions)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function handleTouchStart(event) {
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<style scoped>
|
|
70
|
+
|
|
71
|
+
</style>
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import { ref, unref, reactive, computed, inject, provide, watch } from "vue"
|
|
2
|
+
|
|
3
|
+
export function useFlow(options) {
|
|
4
|
+
const existingFlow = inject("flow")
|
|
5
|
+
if(existingFlow && options) throw new Error("Flow already exists")
|
|
6
|
+
if(existingFlow) return existingFlow
|
|
7
|
+
|
|
8
|
+
const nodes = options?.nodes ?? ref([])
|
|
9
|
+
const edges = options?.edges ?? ref([])
|
|
10
|
+
|
|
11
|
+
const dragState = ref(null)
|
|
12
|
+
const container = ref(null)
|
|
13
|
+
const viewport = ref(null)
|
|
14
|
+
|
|
15
|
+
const nodeViews = reactive(new Map())
|
|
16
|
+
|
|
17
|
+
const size = reactive({
|
|
18
|
+
width: options.width ?? 1024,
|
|
19
|
+
height: options.height ?? 1024,
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const position = reactive({ x: 0, y: 0 })
|
|
23
|
+
const scale = ref(1)
|
|
24
|
+
|
|
25
|
+
function updatePositionAndScale(x, y, s) {
|
|
26
|
+
const viewportRect = viewport.value.getBoundingClientRect()
|
|
27
|
+
const vWidth = viewportRect.width
|
|
28
|
+
const vHeight = viewportRect.height
|
|
29
|
+
if(s < vWidth / size.width) s = vWidth / size.width
|
|
30
|
+
if(s < vHeight / size.height) s = vHeight / size.height
|
|
31
|
+
if(options.maxZoom && s > options.maxZoom) s = options.maxZoom
|
|
32
|
+
if(options.minZoom && s < options.minZoom) s = options.minZoom
|
|
33
|
+
|
|
34
|
+
if(x > 0) x = 0
|
|
35
|
+
if(y > 0) y = 0
|
|
36
|
+
if(x + size.width * s < vWidth) x = vWidth - size.width * s
|
|
37
|
+
if(y + size.height * s < vHeight) y = vHeight - size.height * s
|
|
38
|
+
position.x = x
|
|
39
|
+
position.y = y
|
|
40
|
+
scale.value = s
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function zoom(event, by) {
|
|
44
|
+
const viewportRect = viewport.value.getBoundingClientRect()
|
|
45
|
+
const vWidth = viewportRect.width
|
|
46
|
+
const vHeight = viewportRect.height
|
|
47
|
+
|
|
48
|
+
const zoomEventX = event.clientX - viewportRect.x
|
|
49
|
+
const zoomEventY = event.clientY - viewportRect.y
|
|
50
|
+
|
|
51
|
+
const zoomCenterX = zoomEventX / scale.value - position.x / scale.value
|
|
52
|
+
const zoomCenterY = zoomEventY / scale.value - position.y / scale.value
|
|
53
|
+
|
|
54
|
+
let s = scale.value * by
|
|
55
|
+
|
|
56
|
+
if(s < vWidth / size.width) s = vWidth / size.width
|
|
57
|
+
if(s < vHeight / size.height) s = vHeight / size.height
|
|
58
|
+
if(options.maxZoom && s > options.maxZoom) s = options.maxZoom
|
|
59
|
+
if(options.minZoom && s < options.minZoom) s = options.minZoom
|
|
60
|
+
|
|
61
|
+
const newBy = s / scale.value
|
|
62
|
+
|
|
63
|
+
const newZoomCenterX = zoomCenterX * newBy
|
|
64
|
+
const newZoomCenterY = zoomCenterY * newBy
|
|
65
|
+
|
|
66
|
+
const dx = (newZoomCenterX - zoomCenterX) * scale.value
|
|
67
|
+
const dy = (newZoomCenterY - zoomCenterY) * scale.value
|
|
68
|
+
|
|
69
|
+
updatePositionAndScale(position.x - dx, position.y - dy, s)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getEventPosition(event) {
|
|
73
|
+
const containerRect = container.value.getBoundingClientRect()
|
|
74
|
+
return {
|
|
75
|
+
x: (event.clientX - containerRect.left) / scale.value,
|
|
76
|
+
y: (event.clientY - containerRect.top) / scale.value
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function getElementPosition(element, at = [0,0]) {
|
|
81
|
+
const elementRect = element.getBoundingClientRect()
|
|
82
|
+
const containerRect = container.value.getBoundingClientRect()
|
|
83
|
+
return {
|
|
84
|
+
x: (elementRect.left + elementRect.width * at[0] - containerRect.left) / scale.value,
|
|
85
|
+
y: (elementRect.top + elementRect.height * at[1] - containerRect.top) / scale.value,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getElementRect(element) {
|
|
90
|
+
const containerRect = container.value.getBoundingClientRect()
|
|
91
|
+
const elementRect = element.getBoundingClientRect()
|
|
92
|
+
return {
|
|
93
|
+
x: (elementRect.left - containerRect.left)/scale.value,
|
|
94
|
+
y: (elementRect.top - containerRect.top)/scale.value,
|
|
95
|
+
width: elementRect.width / scale.value,
|
|
96
|
+
height: elementRect.height / scale.value,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function findNode(nodeId) {
|
|
101
|
+
if(options.findNode) return options.findNode(nodeId)
|
|
102
|
+
if(options.findNodeIndex) return unref(nodes)[options.findNodeIndex(nodeId)]
|
|
103
|
+
return unref(nodes).value.find(n => n.id == node)
|
|
104
|
+
}
|
|
105
|
+
function findNodeIndex(nodeId) {
|
|
106
|
+
nodeId = getNodeId(nodeId)
|
|
107
|
+
if(options.findNodeIndex) return options.findNodeIndex(nodeId)
|
|
108
|
+
return unref(nodes).value.findIndex(n => n.id == node)
|
|
109
|
+
}
|
|
110
|
+
function getNodeId(nodeId) {
|
|
111
|
+
if(typeof nodeId == "string") return nodeId
|
|
112
|
+
if(options.getNodeId) return options.getNodeId(nodeId)
|
|
113
|
+
return nodeId.id
|
|
114
|
+
}
|
|
115
|
+
function getNode(node) {
|
|
116
|
+
if(typeof node == "string") return findNode(node)
|
|
117
|
+
return node
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function findEdge(edgeId) {
|
|
121
|
+
if(options.findEdge) return options.findEdge(edgeId)
|
|
122
|
+
if(options.findEdgeIndex) return unref(edges)[options.findEdgeIndex(edgeId)]
|
|
123
|
+
return unref(edges).value.find(e => e.id == edgeId)
|
|
124
|
+
}
|
|
125
|
+
function findEdgeIndex(edgeId) {
|
|
126
|
+
edgeId = getEdgeId(edgeId)
|
|
127
|
+
if(options.findEdgeIndex) return options.findEdgeIndex(edgeId)
|
|
128
|
+
return unref(edges).value.findIndex(e => e.id == edgeId)
|
|
129
|
+
}
|
|
130
|
+
function getEdgeId(edgeId) {
|
|
131
|
+
if(typeof edgeId == "string") return edgeId
|
|
132
|
+
if(options.getEdgeId) return options.getEdgeId(edgeId)
|
|
133
|
+
return edgeId.id
|
|
134
|
+
}
|
|
135
|
+
function getEdge(edge) {
|
|
136
|
+
if(typeof edge == "string") return findEdge(edge)
|
|
137
|
+
return edge
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function setNodeElement(nodeId, element) {
|
|
141
|
+
nodeId = getNodeId(nodeId)
|
|
142
|
+
//console.log("setNodeElement", nodeId, element)
|
|
143
|
+
if(!element) {
|
|
144
|
+
nodeViews.delete(nodeId)
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
const nodeView = nodeViews.get(nodeId)
|
|
148
|
+
if(!nodeView) {
|
|
149
|
+
nodeViews.set(nodeId, {
|
|
150
|
+
node: nodeId,
|
|
151
|
+
element,
|
|
152
|
+
rect: getElementRect(element),
|
|
153
|
+
ports: reactive(new Map()),
|
|
154
|
+
})
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
nodeView.element = element
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function setPortElement(nodeId, portId, element, direction) {
|
|
161
|
+
nodeId = getNodeId(nodeId)
|
|
162
|
+
if (typeof portId != "string") portId = portId.id ?? `${nodeId}/${portId.name}`
|
|
163
|
+
console.log("setPortElement", nodeId, portId, element)
|
|
164
|
+
const nodeView = nodeViews.get(nodeId)
|
|
165
|
+
if(!nodeView) {
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
if(!element) {
|
|
169
|
+
nodeView.ports.delete(portId)
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
nodeView.ports.set(portId, {
|
|
173
|
+
port: portId,
|
|
174
|
+
element,
|
|
175
|
+
rect: getElementRect(element),
|
|
176
|
+
direction,
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function invalidatePortView(portView) {
|
|
181
|
+
portView.rect = getElementRect(portView.element)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function invalidateNodeView(nodeView) {
|
|
185
|
+
if(!nodeView.element) {
|
|
186
|
+
if(typeof nodeView == "string") {
|
|
187
|
+
nodeView = nodeViews.get(nodeView)
|
|
188
|
+
} else {
|
|
189
|
+
nodeView = nodeViews.get(getNodeId(nodeView))
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
nodeView.rect = getElementRect(nodeView.element)
|
|
193
|
+
for(const portView of nodeView.ports.values()) {
|
|
194
|
+
invalidatePortView(portView)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function getPortView(nodeId, portId) {
|
|
199
|
+
nodeId = getNodeId(nodeId)
|
|
200
|
+
if (typeof portId != "string") portId = portId.id ?? `${nodeId}/${portId.name}`
|
|
201
|
+
const nodeView = nodeViews.get(nodeId)
|
|
202
|
+
if(!nodeView) return null
|
|
203
|
+
return nodeView.ports.get(portId)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function getNearestPortView(position, maxDistance = 50, filter = (nodeId, portId) => true) {
|
|
207
|
+
const maxNodeDistance = maxDistance * 2
|
|
208
|
+
const maxDistanceSqr = maxDistance ** 2
|
|
209
|
+
let nearestPortView = null
|
|
210
|
+
let nearestNodeView = null
|
|
211
|
+
let nearestDistanceSqr = Infinity
|
|
212
|
+
for(const nodeView of nodeViews.values()) {
|
|
213
|
+
// check if position is near node
|
|
214
|
+
const rect = nodeView.rect
|
|
215
|
+
//console.log("getNearestPortView", nodeView.node, nodeView.rect)
|
|
216
|
+
if(position.x < rect.x - maxNodeDistance) continue
|
|
217
|
+
if(position.x > rect.x + rect.width + maxNodeDistance) continue
|
|
218
|
+
if(position.y < rect.y - maxNodeDistance) continue
|
|
219
|
+
if(position.y > rect.y + rect.height + maxNodeDistance) continue
|
|
220
|
+
//console.log('dist ok')
|
|
221
|
+
if(filter(nodeView.node, null) === false) continue
|
|
222
|
+
//console.log('node ok')
|
|
223
|
+
// check all ports
|
|
224
|
+
for(const portView of nodeView.ports.values()) {
|
|
225
|
+
const portPosition = {
|
|
226
|
+
x: portView.rect.x + portView.rect.width / 2,
|
|
227
|
+
y: portView.rect.y + portView.rect.height / 2,
|
|
228
|
+
}
|
|
229
|
+
//console.log("port", portView.port, portPosition)
|
|
230
|
+
const distanceSqr = (position.x - portPosition.x) ** 2 + (position.y - portPosition.y) ** 2
|
|
231
|
+
if(distanceSqr > maxDistanceSqr) continue
|
|
232
|
+
//console.log("port dist ok")
|
|
233
|
+
if(filter(nodeView.node, portView.port) === false) continue
|
|
234
|
+
if(distanceSqr < nearestDistanceSqr) {
|
|
235
|
+
nearestPortView = portView
|
|
236
|
+
nearestNodeView = nodeView
|
|
237
|
+
nearestDistanceSqr = distanceSqr
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
portView: nearestPortView,
|
|
243
|
+
nodeView: nearestNodeView,
|
|
244
|
+
distance: Math.sqrt(nearestDistanceSqr),
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
watch(() => container.value, () => {
|
|
249
|
+
for(const nodeView of nodeViews.values()) {
|
|
250
|
+
invalidateNodeView(nodeView)
|
|
251
|
+
}
|
|
252
|
+
}, { immediate: true })
|
|
253
|
+
|
|
254
|
+
function startDragNode(node, event) {
|
|
255
|
+
node = getNode(node)
|
|
256
|
+
dragState.value = {
|
|
257
|
+
type: "dragNode",
|
|
258
|
+
node,
|
|
259
|
+
initialPosition: {
|
|
260
|
+
x: node.position.x,
|
|
261
|
+
y: node.position.y
|
|
262
|
+
},
|
|
263
|
+
startPosition: getEventPosition(event)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function startDrawEdge(nodeId, portId, event, newEdgeOptions) {
|
|
268
|
+
console.log("startDrawEdge", nodeId, portId, event)
|
|
269
|
+
nodeId = getNodeId(nodeId)
|
|
270
|
+
if (typeof portId != "string") portId = portId.id ?? `${node.id}/${portId.name}`
|
|
271
|
+
dragState.value = {
|
|
272
|
+
type: "drawEdge",
|
|
273
|
+
node: nodeId,
|
|
274
|
+
port: portId,
|
|
275
|
+
edge: newEdgeOptions
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function deleteEdge(edgeId) {
|
|
280
|
+
edgeId = getEdgeId(edgeId)
|
|
281
|
+
const edgeIndex = findEdgeIndex(edgeId)
|
|
282
|
+
if(edgeIndex < 0) return
|
|
283
|
+
if(options.deleteEdge) {
|
|
284
|
+
options.deleteEdge(unref(edges)[edgeIndex])
|
|
285
|
+
} else {
|
|
286
|
+
edges.splice(edgeIndex, 1)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function deleteNode(nodeId) {
|
|
291
|
+
nodeId = getNodeId(nodeId)
|
|
292
|
+
const nodeIndex = findNodeIndex(nodeId)
|
|
293
|
+
if(nodeIndex < 0) return
|
|
294
|
+
const edgesToDelete = unref(edges).filter(e => e.src.node == nodeId || e.dest.node == nodeId)
|
|
295
|
+
for(const edge of edgesToDelete) {
|
|
296
|
+
deleteEdge(edge)
|
|
297
|
+
}
|
|
298
|
+
if(options.deleteNode) {
|
|
299
|
+
options.deleteNode(unref(nodes)[nodeIndex])
|
|
300
|
+
} else {
|
|
301
|
+
nodes.splice(nodeIndex, 1)
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function startDragEdgeEnd(edge, end, event) {
|
|
306
|
+
edge = getEdge(edge)
|
|
307
|
+
deleteEdge(edge)
|
|
308
|
+
const otherEnd = {
|
|
309
|
+
'dest': 'src',
|
|
310
|
+
'src': 'dest',
|
|
311
|
+
}[end]
|
|
312
|
+
dragState.value = {
|
|
313
|
+
type: "drawEdge",
|
|
314
|
+
node: edge[otherEnd].node,
|
|
315
|
+
port: edge[otherEnd].port,
|
|
316
|
+
edge
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
function updateNode(nodeId, value) {
|
|
322
|
+
nodeId = getNodeId(nodeId)
|
|
323
|
+
const node = findNode(nodeId)
|
|
324
|
+
//console.log("updateNode", nodeId, node, nodes, JSON.stringify(node, null, 2))
|
|
325
|
+
if(options.updateNode) {
|
|
326
|
+
if (typeof value == "function") {
|
|
327
|
+
options.updateNode(node, value)
|
|
328
|
+
} else {
|
|
329
|
+
options.updateNode(node, (node) => {
|
|
330
|
+
for (const key in value) {
|
|
331
|
+
if (key != "id") node[key] = value[key]
|
|
332
|
+
}
|
|
333
|
+
return node
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
} else {
|
|
337
|
+
if (typeof value == "function") {
|
|
338
|
+
value(node)
|
|
339
|
+
} else {
|
|
340
|
+
for (const key in value) {
|
|
341
|
+
if (key != "id") node[key] = value[key]
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
//console.log("updatedNode", nodeId, node, nodes, JSON.stringify(node, null, 2))
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
let nextEdgeId = 0
|
|
349
|
+
function generateEdgeId() {
|
|
350
|
+
if(options.uidGenerator) return options.uidGenerator()
|
|
351
|
+
return `e${(nextEdgeId++).toFixed().padStart(4, "0")}`
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
let nextNodeId = 0
|
|
355
|
+
function generateNodeId() {
|
|
356
|
+
if(options.uidGenerator) return options.uidGenerator()
|
|
357
|
+
return `n${(nextNodeId++).toFixed().padStart(4, "0")}`
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
let nextPortId = 0
|
|
361
|
+
function generatePortId() {
|
|
362
|
+
if(options.uidGenerator) return options.uidGenerator()
|
|
363
|
+
return `p${(nextPortId++).toFixed().padStart(4, "0")}`
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const flow = {
|
|
367
|
+
nodes,
|
|
368
|
+
edges,
|
|
369
|
+
|
|
370
|
+
dragState,
|
|
371
|
+
container,
|
|
372
|
+
viewport,
|
|
373
|
+
|
|
374
|
+
nodeViews,
|
|
375
|
+
|
|
376
|
+
position, size, scale,
|
|
377
|
+
|
|
378
|
+
options,
|
|
379
|
+
|
|
380
|
+
findNodeIndex,
|
|
381
|
+
findNode,
|
|
382
|
+
getNodeId,
|
|
383
|
+
getNode,
|
|
384
|
+
|
|
385
|
+
updateNode,
|
|
386
|
+
deleteNode,
|
|
387
|
+
deleteEdge,
|
|
388
|
+
|
|
389
|
+
startDragNode,
|
|
390
|
+
startDrawEdge,
|
|
391
|
+
startDragEdgeEnd,
|
|
392
|
+
updatePositionAndScale,
|
|
393
|
+
zoom,
|
|
394
|
+
|
|
395
|
+
getPortView,
|
|
396
|
+
getNearestPortView,
|
|
397
|
+
|
|
398
|
+
invalidateNodeView,
|
|
399
|
+
invalidatePortView,
|
|
400
|
+
|
|
401
|
+
generateEdgeId,
|
|
402
|
+
generateNodeId,
|
|
403
|
+
generatePortId,
|
|
404
|
+
|
|
405
|
+
setNodeElement,
|
|
406
|
+
setPortElement,
|
|
407
|
+
|
|
408
|
+
getEventPosition,
|
|
409
|
+
getElementPosition,
|
|
410
|
+
getElementRect,
|
|
411
|
+
}
|
|
412
|
+
provide("flow", flow)
|
|
413
|
+
|
|
414
|
+
globalThis.flow = flow
|
|
415
|
+
|
|
416
|
+
return flow
|
|
417
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createMemoryHistory,
|
|
3
|
+
createRouter as _createRouter,
|
|
4
|
+
createWebHistory
|
|
5
|
+
} from 'vue-router'
|
|
6
|
+
|
|
7
|
+
import { dbAdminRoutes } from "@live-change/db-admin"
|
|
8
|
+
|
|
9
|
+
export function wysiwygRoutes(config = {}) {
|
|
10
|
+
const { prefix = '/', route = (r) => r } = config
|
|
11
|
+
return [
|
|
12
|
+
|
|
13
|
+
/*route({
|
|
14
|
+
name: 'wysiwyg:editorWithPreview', path: prefix + '', meta: { },
|
|
15
|
+
component: () => import("./EditorWithPreview.vue"),
|
|
16
|
+
props: {
|
|
17
|
+
}
|
|
18
|
+
}),*/
|
|
19
|
+
|
|
20
|
+
...dbAdminRoutes({ prefix: '/_db', route: r => ({ ...r, meta: { ...r.meta, raw: true }}) })
|
|
21
|
+
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function sitemap(route, api) {
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
export function createRouter(app, config) {
|
|
31
|
+
const router = _createRouter({
|
|
32
|
+
history: import.meta.env.SSR ? createMemoryHistory() : createWebHistory(),
|
|
33
|
+
routes: wysiwygRoutes(config)
|
|
34
|
+
})
|
|
35
|
+
return router
|
|
36
|
+
}
|
|
37
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
|
|
3
|
+
import baseViteConfig from '@live-change/frontend-base/vite-config.js'
|
|
4
|
+
|
|
5
|
+
export default defineConfig(async ({ command, mode }) => {
|
|
6
|
+
const baseConfig = (await baseViteConfig({ command, mode }))
|
|
7
|
+
return {
|
|
8
|
+
...baseConfig,
|
|
9
|
+
|
|
10
|
+
resolve: {
|
|
11
|
+
alias: [
|
|
12
|
+
...baseConfig.resolve.alias,
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
})
|
package/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useFlow } from './front/src/components/index.js'
|
|
2
|
+
|
|
3
|
+
import Edge from './front/src/components/Edge.vue'
|
|
4
|
+
import Node from './front/src/components/Node.vue'
|
|
5
|
+
import Flow from './front/src/components/Flow.vue'
|
|
6
|
+
import NodeHandle from './front/src/components/NodeHandle.vue'
|
|
7
|
+
import NodePort from './front/src/components/NodePort.vue'
|
|
8
|
+
import EdgeEndHandle from './front/src/components/EdgeEndHandle.vue'
|
|
9
|
+
import EdgeBezierCurve from './front/src/components/EdgeBezierCurve.vue'
|
|
10
|
+
|
|
11
|
+
export { useFlow, Edge, Node, Flow, NodeHandle, NodePort, EdgeEndHandle, EdgeBezierCurve }
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@live-change/flow-frontend",
|
|
3
|
+
"version": "0.3.15",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"memDev": "lcli memDev --enableSessions --initScript ./init.js --dbAccess",
|
|
6
|
+
"localDevInit": "rm tmp.db; lcli localDev --enableSessions --initScript ./init.js",
|
|
7
|
+
"localDev": "lcli localDev --enableSessions",
|
|
8
|
+
"dev": "lcli dev --enableSessions",
|
|
9
|
+
"ssrDev": "lcli ssrDev --enableSessions",
|
|
10
|
+
"serveAllMem": "cross-env NODE_ENV=production lcli ssrServer --withApi --withServices --updateServices --enableSessions --withDb --dbBackend mem --createDb",
|
|
11
|
+
"serveAll": "cross-env NODE_ENV=production lcli ssrServer --withApi --withServices --updateServices --enableSessions",
|
|
12
|
+
"serve": "cross-env NODE_ENV=production lcli ssrServer --enableSessions",
|
|
13
|
+
"apiServer": "lcli apiServer --enableSessions",
|
|
14
|
+
"devApiServer": "lcli devApiServer --enableSessions",
|
|
15
|
+
"memApiServer": "lcli memApiServer --enableSessions",
|
|
16
|
+
"build": "cd front; yarn build:client && yarn build:server",
|
|
17
|
+
"build:client": "cd front; vite build --ssrManifest --outDir dist/client",
|
|
18
|
+
"build:server": "cd front; vite build --ssr src/entry-server.js --outDir dist/server",
|
|
19
|
+
"generate": "vite build --ssrManifest --outDir dist/static && yarn build:server && node prerender",
|
|
20
|
+
"debug": "node --inspect-brk server"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@live-change/access-control-service": "0.3.36",
|
|
24
|
+
"@live-change/cli": "0.7.34",
|
|
25
|
+
"@live-change/dao": "0.5.22",
|
|
26
|
+
"@live-change/dao-vue3": "0.5.22",
|
|
27
|
+
"@live-change/dao-websocket": "0.5.22",
|
|
28
|
+
"@live-change/db-admin": "0.6.23",
|
|
29
|
+
"@live-change/framework": "0.7.34",
|
|
30
|
+
"@live-change/frontend-base": "0.3.15",
|
|
31
|
+
"@live-change/password-authentication-service": "0.3.36",
|
|
32
|
+
"@live-change/secret-code-service": "0.3.36",
|
|
33
|
+
"@live-change/secret-link-service": "0.3.36",
|
|
34
|
+
"@live-change/session-service": "0.3.36",
|
|
35
|
+
"@live-change/user-frontend": "0.3.15",
|
|
36
|
+
"@live-change/user-service": "0.3.36",
|
|
37
|
+
"@live-change/vue3-components": "0.2.31",
|
|
38
|
+
"@live-change/vue3-ssr": "0.2.31",
|
|
39
|
+
"@vueuse/core": "^10.4.1",
|
|
40
|
+
"codeceptjs-assert": "^0.0.5",
|
|
41
|
+
"compression": "^1.7.4",
|
|
42
|
+
"cross-env": "^7.0.3",
|
|
43
|
+
"get-port-sync": "1.0.1",
|
|
44
|
+
"primeflex": "^3.3.1",
|
|
45
|
+
"primeicons": "^6.0.1",
|
|
46
|
+
"primevue": "^3.40.1",
|
|
47
|
+
"rollup-plugin-node-builtins": "^2.1.2",
|
|
48
|
+
"rollup-plugin-visualizer": "5.9.2",
|
|
49
|
+
"serialize-javascript": "^6.0.1",
|
|
50
|
+
"serve-static": "^1.15.0",
|
|
51
|
+
"vue-router": "^4.2.4",
|
|
52
|
+
"vue3-scroll-border": "0.1.5"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@live-change/codeceptjs-helper": "0.7.34",
|
|
56
|
+
"@wdio/selenium-standalone-service": "^8.15.0",
|
|
57
|
+
"codeceptjs": "^3.5.4",
|
|
58
|
+
"generate-password": "1.7.0",
|
|
59
|
+
"playwright": "^1.37.1",
|
|
60
|
+
"random-profile-generator": "^2.3.0",
|
|
61
|
+
"txtgen": "^3.0.6",
|
|
62
|
+
"webdriverio": "^8.16.6"
|
|
63
|
+
},
|
|
64
|
+
"author": "",
|
|
65
|
+
"license": "BSD-3-Clause",
|
|
66
|
+
"description": "",
|
|
67
|
+
"gitHead": "61ea1e9554f3bf8cb8f9debffdabd8a88855c3b2"
|
|
68
|
+
}
|