@pyreon/flow 0.10.0 → 0.11.1
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/lib/analysis/index.js.html +1 -1
- package/lib/{elk.bundled-B9dPTHTZ.js → elk.bundled-wgxWJMYd.js} +2 -2
- package/lib/elk.bundled-wgxWJMYd.js.map +1 -0
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +13 -13
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +14 -7
- package/src/components/background.tsx +9 -14
- package/src/components/controls.tsx +11 -18
- package/src/components/flow-component.tsx +57 -109
- package/src/components/handle.tsx +8 -8
- package/src/components/minimap.tsx +10 -27
- package/src/components/node-resizer.tsx +25 -25
- package/src/components/node-toolbar.tsx +12 -21
- package/src/components/panel.tsx +9 -9
- package/src/edges.ts +16 -47
- package/src/flow.ts +32 -88
- package/src/index.ts +17 -17
- package/src/layout.ts +23 -31
- package/src/tests/flow-advanced.test.ts +191 -200
- package/src/tests/flow.test.ts +418 -468
- package/src/types.ts +28 -42
- package/lib/elk.bundled-B9dPTHTZ.js.map +0 -1
package/src/edges.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { EdgePathResult, FlowNode, XYPosition } from
|
|
2
|
-
import { Position } from
|
|
1
|
+
import type { EdgePathResult, FlowNode, XYPosition } from "./types"
|
|
2
|
+
import { Position } from "./types"
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Auto-detect the best handle position based on relative node positions.
|
|
@@ -148,10 +148,7 @@ export function getBezierPath(params: {
|
|
|
148
148
|
break
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
const center = getCenter(
|
|
152
|
-
{ x: sourceX, y: sourceY },
|
|
153
|
-
{ x: targetX, y: targetY },
|
|
154
|
-
)
|
|
151
|
+
const center = getCenter({ x: sourceX, y: sourceY }, { x: targetX, y: targetY })
|
|
155
152
|
|
|
156
153
|
return {
|
|
157
154
|
path: `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`,
|
|
@@ -184,46 +181,25 @@ export function getSmoothStepPath(params: {
|
|
|
184
181
|
offset = 20,
|
|
185
182
|
} = params
|
|
186
183
|
|
|
187
|
-
const isHorizontalSource =
|
|
188
|
-
|
|
189
|
-
const isHorizontalTarget =
|
|
190
|
-
targetPosition === Position.Left || targetPosition === Position.Right
|
|
184
|
+
const isHorizontalSource = sourcePosition === Position.Left || sourcePosition === Position.Right
|
|
185
|
+
const isHorizontalTarget = targetPosition === Position.Left || targetPosition === Position.Right
|
|
191
186
|
|
|
192
187
|
// Calculate offset points
|
|
193
188
|
const sourceOffsetX =
|
|
194
|
-
sourcePosition === Position.Right
|
|
195
|
-
? offset
|
|
196
|
-
: sourcePosition === Position.Left
|
|
197
|
-
? -offset
|
|
198
|
-
: 0
|
|
189
|
+
sourcePosition === Position.Right ? offset : sourcePosition === Position.Left ? -offset : 0
|
|
199
190
|
const sourceOffsetY =
|
|
200
|
-
sourcePosition === Position.Bottom
|
|
201
|
-
? offset
|
|
202
|
-
: sourcePosition === Position.Top
|
|
203
|
-
? -offset
|
|
204
|
-
: 0
|
|
191
|
+
sourcePosition === Position.Bottom ? offset : sourcePosition === Position.Top ? -offset : 0
|
|
205
192
|
const targetOffsetX =
|
|
206
|
-
targetPosition === Position.Right
|
|
207
|
-
? offset
|
|
208
|
-
: targetPosition === Position.Left
|
|
209
|
-
? -offset
|
|
210
|
-
: 0
|
|
193
|
+
targetPosition === Position.Right ? offset : targetPosition === Position.Left ? -offset : 0
|
|
211
194
|
const targetOffsetY =
|
|
212
|
-
targetPosition === Position.Bottom
|
|
213
|
-
? offset
|
|
214
|
-
: targetPosition === Position.Top
|
|
215
|
-
? -offset
|
|
216
|
-
: 0
|
|
195
|
+
targetPosition === Position.Bottom ? offset : targetPosition === Position.Top ? -offset : 0
|
|
217
196
|
|
|
218
197
|
const sX = sourceX + sourceOffsetX
|
|
219
198
|
const sY = sourceY + sourceOffsetY
|
|
220
199
|
const tX = targetX + targetOffsetX
|
|
221
200
|
const tY = targetY + targetOffsetY
|
|
222
201
|
|
|
223
|
-
const center = getCenter(
|
|
224
|
-
{ x: sourceX, y: sourceY },
|
|
225
|
-
{ x: targetX, y: targetY },
|
|
226
|
-
)
|
|
202
|
+
const center = getCenter({ x: sourceX, y: sourceY }, { x: targetX, y: targetY })
|
|
227
203
|
|
|
228
204
|
// Simple smoothstep: source → midpoint → target with rounded corners
|
|
229
205
|
const midX = (sX + tX) / 2
|
|
@@ -261,10 +237,7 @@ export function getStraightPath(params: {
|
|
|
261
237
|
targetY: number
|
|
262
238
|
}): EdgePathResult {
|
|
263
239
|
const { sourceX, sourceY, targetX, targetY } = params
|
|
264
|
-
const center = getCenter(
|
|
265
|
-
{ x: sourceX, y: sourceY },
|
|
266
|
-
{ x: targetX, y: targetY },
|
|
267
|
-
)
|
|
240
|
+
const center = getCenter({ x: sourceX, y: sourceY }, { x: targetX, y: targetY })
|
|
268
241
|
|
|
269
242
|
return {
|
|
270
243
|
path: `M${sourceX},${sourceY} L${targetX},${targetY}`,
|
|
@@ -304,14 +277,10 @@ export function getWaypointPath(params: {
|
|
|
304
277
|
return getStraightPath({ sourceX, sourceY, targetX, targetY })
|
|
305
278
|
}
|
|
306
279
|
|
|
307
|
-
const allPoints = [
|
|
308
|
-
{ x: sourceX, y: sourceY },
|
|
309
|
-
...waypoints,
|
|
310
|
-
{ x: targetX, y: targetY },
|
|
311
|
-
]
|
|
280
|
+
const allPoints = [{ x: sourceX, y: sourceY }, ...waypoints, { x: targetX, y: targetY }]
|
|
312
281
|
|
|
313
282
|
const segments = allPoints.map((p) => `${p.x},${p.y}`)
|
|
314
|
-
const path = `M${segments.join(
|
|
283
|
+
const path = `M${segments.join(" L")}`
|
|
315
284
|
|
|
316
285
|
// Label at the middle waypoint
|
|
317
286
|
const midIdx = Math.floor(waypoints.length / 2)
|
|
@@ -336,7 +305,7 @@ export function getEdgePath(
|
|
|
336
305
|
targetPosition: Position,
|
|
337
306
|
): EdgePathResult {
|
|
338
307
|
switch (type) {
|
|
339
|
-
case
|
|
308
|
+
case "smoothstep":
|
|
340
309
|
return getSmoothStepPath({
|
|
341
310
|
sourceX,
|
|
342
311
|
sourceY,
|
|
@@ -345,9 +314,9 @@ export function getEdgePath(
|
|
|
345
314
|
targetY,
|
|
346
315
|
targetPosition,
|
|
347
316
|
})
|
|
348
|
-
case
|
|
317
|
+
case "straight":
|
|
349
318
|
return getStraightPath({ sourceX, sourceY, targetX, targetY })
|
|
350
|
-
case
|
|
319
|
+
case "step":
|
|
351
320
|
return getStepPath({
|
|
352
321
|
sourceX,
|
|
353
322
|
sourceY,
|
package/src/flow.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { batch, computed, signal } from
|
|
2
|
-
import { computeLayout } from
|
|
1
|
+
import { batch, computed, signal } from "@pyreon/reactivity"
|
|
2
|
+
import { computeLayout } from "./layout"
|
|
3
3
|
import type {
|
|
4
4
|
Connection,
|
|
5
5
|
FlowConfig,
|
|
@@ -10,15 +10,15 @@ import type {
|
|
|
10
10
|
LayoutOptions,
|
|
11
11
|
NodeChange,
|
|
12
12
|
XYPosition,
|
|
13
|
-
} from
|
|
13
|
+
} from "./types"
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Generate a unique edge id from source/target.
|
|
17
17
|
*/
|
|
18
18
|
function edgeId(edge: FlowEdge): string {
|
|
19
19
|
if (edge.id) return edge.id
|
|
20
|
-
const sh = edge.sourceHandle ? `-${edge.sourceHandle}` :
|
|
21
|
-
const th = edge.targetHandle ? `-${edge.targetHandle}` :
|
|
20
|
+
const sh = edge.sourceHandle ? `-${edge.sourceHandle}` : ""
|
|
21
|
+
const th = edge.targetHandle ? `-${edge.targetHandle}` : ""
|
|
22
22
|
return `e-${edge.source}${sh}-${edge.target}${th}`
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -51,7 +51,7 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
51
51
|
const {
|
|
52
52
|
nodes: initialNodes = [],
|
|
53
53
|
edges: initialEdges = [],
|
|
54
|
-
defaultEdgeType =
|
|
54
|
+
defaultEdgeType = "bezier",
|
|
55
55
|
minZoom = 0.1,
|
|
56
56
|
maxZoom = 4,
|
|
57
57
|
snapToGrid = false,
|
|
@@ -112,22 +112,18 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
112
112
|
batch(() => {
|
|
113
113
|
nodes.update((nds) => nds.filter((n) => n.id !== id))
|
|
114
114
|
// Remove connected edges
|
|
115
|
-
edges.update((eds) =>
|
|
116
|
-
eds.filter((e) => e.source !== id && e.target !== id),
|
|
117
|
-
)
|
|
115
|
+
edges.update((eds) => eds.filter((e) => e.source !== id && e.target !== id))
|
|
118
116
|
selectedNodeIds.update((set) => {
|
|
119
117
|
const next = new Set(set)
|
|
120
118
|
next.delete(id)
|
|
121
119
|
return next
|
|
122
120
|
})
|
|
123
121
|
})
|
|
124
|
-
emitNodeChanges([{ type:
|
|
122
|
+
emitNodeChanges([{ type: "remove", id }])
|
|
125
123
|
}
|
|
126
124
|
|
|
127
125
|
function updateNode(id: string, update: Partial<FlowNode>): void {
|
|
128
|
-
nodes.update((nds) =>
|
|
129
|
-
nds.map((n) => (n.id === id ? { ...n, ...update } : n)),
|
|
130
|
-
)
|
|
126
|
+
nodes.update((nds) => nds.map((n) => (n.id === id ? { ...n, ...update } : n)))
|
|
131
127
|
}
|
|
132
128
|
|
|
133
129
|
function updateNodePosition(id: string, position: XYPosition): void {
|
|
@@ -142,10 +138,8 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
142
138
|
const node = getNode(id)
|
|
143
139
|
pos = clampToExtent(pos, node?.width, node?.height)
|
|
144
140
|
|
|
145
|
-
nodes.update((nds) =>
|
|
146
|
-
|
|
147
|
-
)
|
|
148
|
-
emitNodeChanges([{ type: 'position', id, position: pos }])
|
|
141
|
+
nodes.update((nds) => nds.map((n) => (n.id === id ? { ...n, position: pos } : n)))
|
|
142
|
+
emitNodeChanges([{ type: "position", id, position: pos }])
|
|
149
143
|
}
|
|
150
144
|
|
|
151
145
|
// ── Edge operations ──────────────────────────────────────────────────────
|
|
@@ -193,7 +187,7 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
193
187
|
const sourceNode = getNode(connection.source)
|
|
194
188
|
if (!sourceNode) return false
|
|
195
189
|
|
|
196
|
-
const sourceType = sourceNode.type ??
|
|
190
|
+
const sourceType = sourceNode.type ?? "default"
|
|
197
191
|
const rule = connectionRules[sourceType]
|
|
198
192
|
if (!rule) return true // no rule = allow
|
|
199
193
|
|
|
@@ -201,7 +195,7 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
201
195
|
const targetNode = getNode(connection.target)
|
|
202
196
|
if (!targetNode) return false
|
|
203
197
|
|
|
204
|
-
const targetType = targetNode.type ??
|
|
198
|
+
const targetType = targetNode.type ?? "default"
|
|
205
199
|
return rule.outputs.includes(targetType)
|
|
206
200
|
}
|
|
207
201
|
|
|
@@ -275,13 +269,8 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
275
269
|
|
|
276
270
|
// ── Viewport ─────────────────────────────────────────────────────────────
|
|
277
271
|
|
|
278
|
-
function fitView(
|
|
279
|
-
nodeIds
|
|
280
|
-
padding = config.fitViewPadding ?? 0.1,
|
|
281
|
-
): void {
|
|
282
|
-
const targetNodes = nodeIds
|
|
283
|
-
? nodes.peek().filter((n) => nodeIds.includes(n.id))
|
|
284
|
-
: nodes.peek()
|
|
272
|
+
function fitView(nodeIds?: string[], padding = config.fitViewPadding ?? 0.1): void {
|
|
273
|
+
const targetNodes = nodeIds ? nodes.peek().filter((n) => nodeIds.includes(n.id)) : nodes.peek()
|
|
285
274
|
|
|
286
275
|
if (targetNodes.length === 0) return
|
|
287
276
|
|
|
@@ -302,8 +291,7 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
302
291
|
const graphWidth = maxX - minX
|
|
303
292
|
const graphHeight = maxY - minY
|
|
304
293
|
|
|
305
|
-
const { width: containerWidth, height: containerHeight } =
|
|
306
|
-
containerSize.peek()
|
|
294
|
+
const { width: containerWidth, height: containerHeight } = containerSize.peek()
|
|
307
295
|
|
|
308
296
|
const zoomX = containerWidth / (graphWidth * (1 + padding * 2))
|
|
309
297
|
const zoomY = containerHeight / (graphHeight * (1 + padding * 2))
|
|
@@ -360,29 +348,19 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
360
348
|
const screenW = w * v.zoom
|
|
361
349
|
const screenH = h * v.zoom
|
|
362
350
|
const { width: cw, height: ch } = containerSize.peek()
|
|
363
|
-
return
|
|
364
|
-
screenX + screenW > 0 &&
|
|
365
|
-
screenX < cw &&
|
|
366
|
-
screenY + screenH > 0 &&
|
|
367
|
-
screenY < ch
|
|
368
|
-
)
|
|
351
|
+
return screenX + screenW > 0 && screenX < cw && screenY + screenH > 0 && screenY < ch
|
|
369
352
|
}
|
|
370
353
|
|
|
371
354
|
// ── Layout ───────────────────────────────────────────────────────────────
|
|
372
355
|
|
|
373
356
|
async function layout(
|
|
374
|
-
algorithm: LayoutAlgorithm =
|
|
357
|
+
algorithm: LayoutAlgorithm = "layered",
|
|
375
358
|
options: LayoutOptions = {},
|
|
376
359
|
): Promise<void> {
|
|
377
360
|
const currentNodes = nodes.peek()
|
|
378
361
|
const currentEdges = edges.peek()
|
|
379
362
|
|
|
380
|
-
const positions = await computeLayout(
|
|
381
|
-
currentNodes,
|
|
382
|
-
currentEdges,
|
|
383
|
-
algorithm,
|
|
384
|
-
options,
|
|
385
|
-
)
|
|
363
|
+
const positions = await computeLayout(currentNodes, currentEdges, algorithm, options)
|
|
386
364
|
|
|
387
365
|
const animate = options.animate !== false
|
|
388
366
|
const duration = options.animationDuration ?? 300
|
|
@@ -400,9 +378,7 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
400
378
|
}
|
|
401
379
|
|
|
402
380
|
// Animated transition — interpolate positions over duration
|
|
403
|
-
const startPositions = new Map(
|
|
404
|
-
currentNodes.map((n) => [n.id, { ...n.position }]),
|
|
405
|
-
)
|
|
381
|
+
const startPositions = new Map(currentNodes.map((n) => [n.id, { ...n.position }]))
|
|
406
382
|
const targetPositions = new Map(positions.map((p) => [p.id, p.position]))
|
|
407
383
|
|
|
408
384
|
const startTime = performance.now()
|
|
@@ -445,9 +421,7 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
445
421
|
// ── Graph queries ────────────────────────────────────────────────────────
|
|
446
422
|
|
|
447
423
|
function getConnectedEdges(nodeId: string): FlowEdge[] {
|
|
448
|
-
return edges
|
|
449
|
-
.peek()
|
|
450
|
-
.filter((e) => e.source === nodeId || e.target === nodeId)
|
|
424
|
+
return edges.peek().filter((e) => e.source === nodeId || e.target === nodeId)
|
|
451
425
|
}
|
|
452
426
|
|
|
453
427
|
function getIncomers(nodeId: string): FlowNode[] {
|
|
@@ -469,9 +443,7 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
469
443
|
return () => connectListeners.delete(callback)
|
|
470
444
|
}
|
|
471
445
|
|
|
472
|
-
function onNodesChange(
|
|
473
|
-
callback: (changes: NodeChange[]) => void,
|
|
474
|
-
): () => void {
|
|
446
|
+
function onNodesChange(callback: (changes: NodeChange[]) => void): () => void {
|
|
475
447
|
nodesChangeListeners.add(callback)
|
|
476
448
|
return () => nodesChangeListeners.delete(callback)
|
|
477
449
|
}
|
|
@@ -738,11 +710,7 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
738
710
|
|
|
739
711
|
// ── Edge waypoints ──────────────────────────────────────────────────────
|
|
740
712
|
|
|
741
|
-
function addEdgeWaypoint(
|
|
742
|
-
edgeIdentifier: string,
|
|
743
|
-
point: XYPosition,
|
|
744
|
-
index?: number,
|
|
745
|
-
): void {
|
|
713
|
+
function addEdgeWaypoint(edgeIdentifier: string, point: XYPosition, index?: number): void {
|
|
746
714
|
edges.update((eds) =>
|
|
747
715
|
eds.map((e) => {
|
|
748
716
|
if (e.id !== edgeIdentifier) return e
|
|
@@ -769,11 +737,7 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
769
737
|
)
|
|
770
738
|
}
|
|
771
739
|
|
|
772
|
-
function updateEdgeWaypoint(
|
|
773
|
-
edgeIdentifier: string,
|
|
774
|
-
index: number,
|
|
775
|
-
point: XYPosition,
|
|
776
|
-
): void {
|
|
740
|
+
function updateEdgeWaypoint(edgeIdentifier: string, index: number, point: XYPosition): void {
|
|
777
741
|
edges.update((eds) =>
|
|
778
742
|
eds.map((e) => {
|
|
779
743
|
if (e.id !== edgeIdentifier) return e
|
|
@@ -788,10 +752,7 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
788
752
|
|
|
789
753
|
// ── Proximity connect ───────────────────────────────────────────────────
|
|
790
754
|
|
|
791
|
-
function getProximityConnection(
|
|
792
|
-
nodeId: string,
|
|
793
|
-
threshold = 50,
|
|
794
|
-
): Connection | null {
|
|
755
|
+
function getProximityConnection(nodeId: string, threshold = 50): Connection | null {
|
|
795
756
|
const node = getNode(nodeId)
|
|
796
757
|
if (!node) return null
|
|
797
758
|
|
|
@@ -888,18 +849,14 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
888
849
|
// Push in the direction of least overlap
|
|
889
850
|
if (overlapX < overlapY) {
|
|
890
851
|
const dx =
|
|
891
|
-
node.position.x < other.position.x
|
|
892
|
-
? -(overlapX + spacing) / 2
|
|
893
|
-
: (overlapX + spacing) / 2
|
|
852
|
+
node.position.x < other.position.x ? -(overlapX + spacing) / 2 : (overlapX + spacing) / 2
|
|
894
853
|
updateNodePosition(other.id, {
|
|
895
854
|
x: other.position.x - dx,
|
|
896
855
|
y: other.position.y,
|
|
897
856
|
})
|
|
898
857
|
} else {
|
|
899
858
|
const dy =
|
|
900
|
-
node.position.y < other.position.y
|
|
901
|
-
? -(overlapY + spacing) / 2
|
|
902
|
-
: (overlapY + spacing) / 2
|
|
859
|
+
node.position.y < other.position.y ? -(overlapY + spacing) / 2 : (overlapY + spacing) / 2
|
|
903
860
|
updateNodePosition(other.id, {
|
|
904
861
|
x: other.position.x,
|
|
905
862
|
y: other.position.y - dy,
|
|
@@ -910,30 +867,17 @@ export function createFlow(config: FlowConfig = {}): FlowInstance {
|
|
|
910
867
|
|
|
911
868
|
// ── Node extent (drag boundaries) ──────────────────────────────────────
|
|
912
869
|
|
|
913
|
-
function setNodeExtent(
|
|
914
|
-
extent: [[number, number], [number, number]] | null,
|
|
915
|
-
): void {
|
|
870
|
+
function setNodeExtent(extent: [[number, number], [number, number]] | null): void {
|
|
916
871
|
nodeExtent = extent
|
|
917
872
|
}
|
|
918
873
|
|
|
919
|
-
let nodeExtent: [[number, number], [number, number]] | null =
|
|
920
|
-
config.nodeExtent ?? null
|
|
874
|
+
let nodeExtent: [[number, number], [number, number]] | null = config.nodeExtent ?? null
|
|
921
875
|
|
|
922
|
-
function clampToExtent(
|
|
923
|
-
position: XYPosition,
|
|
924
|
-
nodeWidth = 150,
|
|
925
|
-
nodeHeight = 40,
|
|
926
|
-
): XYPosition {
|
|
876
|
+
function clampToExtent(position: XYPosition, nodeWidth = 150, nodeHeight = 40): XYPosition {
|
|
927
877
|
if (!nodeExtent) return position
|
|
928
878
|
return {
|
|
929
|
-
x: Math.min(
|
|
930
|
-
|
|
931
|
-
nodeExtent[1][0] - nodeWidth,
|
|
932
|
-
),
|
|
933
|
-
y: Math.min(
|
|
934
|
-
Math.max(position.y, nodeExtent[0][1]),
|
|
935
|
-
nodeExtent[1][1] - nodeHeight,
|
|
936
|
-
),
|
|
879
|
+
x: Math.min(Math.max(position.x, nodeExtent[0][0]), nodeExtent[1][0] - nodeWidth),
|
|
880
|
+
y: Math.min(Math.max(position.y, nodeExtent[0][1]), nodeExtent[1][1] - nodeHeight),
|
|
937
881
|
}
|
|
938
882
|
}
|
|
939
883
|
|
package/src/index.ts
CHANGED
|
@@ -24,18 +24,18 @@
|
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
export { Background } from
|
|
28
|
-
export { Controls } from
|
|
29
|
-
export type { FlowComponentProps } from
|
|
27
|
+
export { Background } from "./components/background"
|
|
28
|
+
export { Controls } from "./components/controls"
|
|
29
|
+
export type { FlowComponentProps } from "./components/flow-component"
|
|
30
30
|
// Components
|
|
31
|
-
export { Flow } from
|
|
32
|
-
export { Handle } from
|
|
33
|
-
export { MiniMap } from
|
|
34
|
-
export type { NodeResizerProps } from
|
|
35
|
-
export { NodeResizer } from
|
|
36
|
-
export type { NodeToolbarProps } from
|
|
37
|
-
export { NodeToolbar } from
|
|
38
|
-
export { Panel } from
|
|
31
|
+
export { Flow } from "./components/flow-component"
|
|
32
|
+
export { Handle } from "./components/handle"
|
|
33
|
+
export { MiniMap } from "./components/minimap"
|
|
34
|
+
export type { NodeResizerProps } from "./components/node-resizer"
|
|
35
|
+
export { NodeResizer } from "./components/node-resizer"
|
|
36
|
+
export type { NodeToolbarProps } from "./components/node-toolbar"
|
|
37
|
+
export { NodeToolbar } from "./components/node-toolbar"
|
|
38
|
+
export { Panel } from "./components/panel"
|
|
39
39
|
// Edge path utilities
|
|
40
40
|
export {
|
|
41
41
|
getBezierPath,
|
|
@@ -46,13 +46,13 @@ export {
|
|
|
46
46
|
getStepPath,
|
|
47
47
|
getStraightPath,
|
|
48
48
|
getWaypointPath,
|
|
49
|
-
} from
|
|
49
|
+
} from "./edges"
|
|
50
50
|
// Core
|
|
51
|
-
export { createFlow } from
|
|
51
|
+
export { createFlow } from "./flow"
|
|
52
52
|
// Layout
|
|
53
|
-
export { computeLayout } from
|
|
53
|
+
export { computeLayout } from "./layout"
|
|
54
54
|
// Styles
|
|
55
|
-
export { flowStyles } from
|
|
55
|
+
export { flowStyles } from "./styles"
|
|
56
56
|
export type {
|
|
57
57
|
BackgroundProps,
|
|
58
58
|
Connection,
|
|
@@ -78,6 +78,6 @@ export type {
|
|
|
78
78
|
Rect,
|
|
79
79
|
Viewport,
|
|
80
80
|
XYPosition,
|
|
81
|
-
} from
|
|
81
|
+
} from "./types"
|
|
82
82
|
// Types
|
|
83
|
-
export { Position } from
|
|
83
|
+
export { Position } from "./types"
|
package/src/layout.ts
CHANGED
|
@@ -1,27 +1,22 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
FlowEdge,
|
|
3
|
-
FlowNode,
|
|
4
|
-
LayoutAlgorithm,
|
|
5
|
-
LayoutOptions,
|
|
6
|
-
} from './types'
|
|
1
|
+
import type { FlowEdge, FlowNode, LayoutAlgorithm, LayoutOptions } from "./types"
|
|
7
2
|
|
|
8
3
|
// ─── ELK algorithm mapping ───────────────────────────────────────────────────
|
|
9
4
|
|
|
10
5
|
const ELK_ALGORITHMS: Record<LayoutAlgorithm, string> = {
|
|
11
|
-
layered:
|
|
12
|
-
force:
|
|
13
|
-
stress:
|
|
14
|
-
tree:
|
|
15
|
-
radial:
|
|
16
|
-
box:
|
|
17
|
-
rectpacking:
|
|
6
|
+
layered: "org.eclipse.elk.layered",
|
|
7
|
+
force: "org.eclipse.elk.force",
|
|
8
|
+
stress: "org.eclipse.elk.stress",
|
|
9
|
+
tree: "org.eclipse.elk.mrtree",
|
|
10
|
+
radial: "org.eclipse.elk.radial",
|
|
11
|
+
box: "org.eclipse.elk.box",
|
|
12
|
+
rectpacking: "org.eclipse.elk.rectpacking",
|
|
18
13
|
}
|
|
19
14
|
|
|
20
15
|
const ELK_DIRECTIONS: Record<string, string> = {
|
|
21
|
-
UP:
|
|
22
|
-
DOWN:
|
|
23
|
-
LEFT:
|
|
24
|
-
RIGHT:
|
|
16
|
+
UP: "UP",
|
|
17
|
+
DOWN: "DOWN",
|
|
18
|
+
LEFT: "LEFT",
|
|
19
|
+
RIGHT: "RIGHT",
|
|
25
20
|
}
|
|
26
21
|
|
|
27
22
|
// ─── Lazy-loaded ELK instance ────────────────────────────────────────────────
|
|
@@ -33,7 +28,7 @@ async function getELK(): Promise<any> {
|
|
|
33
28
|
if (elkInstance) return elkInstance
|
|
34
29
|
if (elkPromise) return elkPromise
|
|
35
30
|
|
|
36
|
-
elkPromise = import(
|
|
31
|
+
elkPromise = import("elkjs/lib/elk.bundled.js").then((mod) => {
|
|
37
32
|
const ELK = mod.default || mod
|
|
38
33
|
elkInstance = new ELK()
|
|
39
34
|
return elkInstance
|
|
@@ -74,35 +69,32 @@ function toElkGraph(
|
|
|
74
69
|
options: LayoutOptions,
|
|
75
70
|
): ElkGraph {
|
|
76
71
|
const layoutOptions: Record<string, string> = {
|
|
77
|
-
|
|
72
|
+
"elk.algorithm": ELK_ALGORITHMS[algorithm] ?? ELK_ALGORITHMS.layered,
|
|
78
73
|
}
|
|
79
74
|
|
|
80
75
|
if (options.direction) {
|
|
81
|
-
layoutOptions[
|
|
76
|
+
layoutOptions["elk.direction"] = ELK_DIRECTIONS[options.direction] ?? "DOWN"
|
|
82
77
|
}
|
|
83
78
|
|
|
84
79
|
if (options.nodeSpacing !== undefined) {
|
|
85
|
-
layoutOptions[
|
|
80
|
+
layoutOptions["elk.spacing.nodeNode"] = String(options.nodeSpacing)
|
|
86
81
|
}
|
|
87
82
|
|
|
88
83
|
if (options.layerSpacing !== undefined) {
|
|
89
|
-
layoutOptions[
|
|
90
|
-
options.layerSpacing,
|
|
91
|
-
)
|
|
84
|
+
layoutOptions["elk.layered.spacing.nodeNodeBetweenLayers"] = String(options.layerSpacing)
|
|
92
85
|
}
|
|
93
86
|
|
|
94
87
|
if (options.edgeRouting) {
|
|
95
88
|
const routingMap: Record<string, string> = {
|
|
96
|
-
orthogonal:
|
|
97
|
-
splines:
|
|
98
|
-
polyline:
|
|
89
|
+
orthogonal: "ORTHOGONAL",
|
|
90
|
+
splines: "SPLINES",
|
|
91
|
+
polyline: "POLYLINE",
|
|
99
92
|
}
|
|
100
|
-
layoutOptions[
|
|
101
|
-
routingMap[options.edgeRouting] ?? 'ORTHOGONAL'
|
|
93
|
+
layoutOptions["elk.edgeRouting"] = routingMap[options.edgeRouting] ?? "ORTHOGONAL"
|
|
102
94
|
}
|
|
103
95
|
|
|
104
96
|
return {
|
|
105
|
-
id:
|
|
97
|
+
id: "root",
|
|
106
98
|
layoutOptions,
|
|
107
99
|
children: nodes.map((node) => ({
|
|
108
100
|
id: node.id,
|
|
@@ -138,7 +130,7 @@ function toElkGraph(
|
|
|
138
130
|
export async function computeLayout(
|
|
139
131
|
nodes: FlowNode[],
|
|
140
132
|
edges: FlowEdge[],
|
|
141
|
-
algorithm: LayoutAlgorithm =
|
|
133
|
+
algorithm: LayoutAlgorithm = "layered",
|
|
142
134
|
options: LayoutOptions = {},
|
|
143
135
|
): Promise<Array<{ id: string; position: { x: number; y: number } }>> {
|
|
144
136
|
const elk = await getELK()
|