@elabs-ai/components-flow 4.2.0 → 5.0.0
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/README.md +2 -1
- package/dist/canvas-shell/canvas-shell.js +13 -0
- package/dist/canvas-shell/canvas-shell.js.map +1 -0
- package/dist/canvas-shell/clamped-fit-offset.js +8 -0
- package/dist/canvas-shell/clamped-fit-offset.js.map +1 -0
- package/dist/canvas-shell/index.js +14 -0
- package/dist/canvas-shell/index.js.map +1 -0
- package/dist/canvas-shell/use-measured-nodes.js +8 -0
- package/dist/canvas-shell/use-measured-nodes.js.map +1 -0
- package/dist/chunk-3BTU2VWZ.js +42 -0
- package/dist/chunk-3BTU2VWZ.js.map +1 -0
- package/dist/chunk-3LK6OOXE.js +2 -0
- package/dist/chunk-3LK6OOXE.js.map +1 -0
- package/dist/chunk-3M3FQAD4.js +105 -0
- package/dist/chunk-3M3FQAD4.js.map +1 -0
- package/dist/chunk-3Y5TCGC3.js +2 -0
- package/dist/chunk-3Y5TCGC3.js.map +1 -0
- package/dist/chunk-4VYHZM3S.js +108 -0
- package/dist/chunk-4VYHZM3S.js.map +1 -0
- package/dist/chunk-5TVVM4GR.js +45 -0
- package/dist/chunk-5TVVM4GR.js.map +1 -0
- package/dist/chunk-5XT73MQQ.js +2 -0
- package/dist/chunk-5XT73MQQ.js.map +1 -0
- package/dist/chunk-6LQD5Y5R.js +24 -0
- package/dist/chunk-6LQD5Y5R.js.map +1 -0
- package/dist/chunk-6SR26QY2.js +18 -0
- package/dist/chunk-6SR26QY2.js.map +1 -0
- package/dist/chunk-75JJK6BH.js +157 -0
- package/dist/chunk-75JJK6BH.js.map +1 -0
- package/dist/chunk-A66JBW4X.js +2 -0
- package/dist/chunk-A66JBW4X.js.map +1 -0
- package/dist/chunk-A7AFM3LI.js +72 -0
- package/dist/chunk-A7AFM3LI.js.map +1 -0
- package/dist/chunk-ADHLQF56.js +2 -0
- package/dist/chunk-ADHLQF56.js.map +1 -0
- package/dist/chunk-ATO4LXJF.js +75 -0
- package/dist/chunk-ATO4LXJF.js.map +1 -0
- package/dist/chunk-ATXVXTMZ.js +227 -0
- package/dist/chunk-ATXVXTMZ.js.map +1 -0
- package/dist/chunk-B6IUPZU7.js +2 -0
- package/dist/chunk-B6IUPZU7.js.map +1 -0
- package/dist/chunk-BJ7UMCD3.js +53 -0
- package/dist/chunk-BJ7UMCD3.js.map +1 -0
- package/dist/chunk-BWGYV4JZ.js +2 -0
- package/dist/chunk-BWGYV4JZ.js.map +1 -0
- package/dist/chunk-CWNO63LQ.js +109 -0
- package/dist/chunk-CWNO63LQ.js.map +1 -0
- package/dist/chunk-DHPUXKCZ.js +76 -0
- package/dist/chunk-DHPUXKCZ.js.map +1 -0
- package/dist/chunk-DNX7G3KA.js +185 -0
- package/dist/chunk-DNX7G3KA.js.map +1 -0
- package/dist/chunk-DNZRAJAQ.js +41 -0
- package/dist/chunk-DNZRAJAQ.js.map +1 -0
- package/dist/chunk-ELCLRVD4.js +2 -0
- package/dist/chunk-ELCLRVD4.js.map +1 -0
- package/dist/chunk-ELOADZSX.js +2 -0
- package/dist/chunk-ELOADZSX.js.map +1 -0
- package/dist/chunk-EUNV2MR5.js +21 -0
- package/dist/chunk-EUNV2MR5.js.map +1 -0
- package/dist/chunk-FAFQ5KLB.js +43 -0
- package/dist/chunk-FAFQ5KLB.js.map +1 -0
- package/dist/chunk-FIRO6Y3R.js +128 -0
- package/dist/chunk-FIRO6Y3R.js.map +1 -0
- package/dist/chunk-GE73WQUK.js +2 -0
- package/dist/chunk-GE73WQUK.js.map +1 -0
- package/dist/chunk-GIARAOU6.js +39 -0
- package/dist/chunk-GIARAOU6.js.map +1 -0
- package/dist/chunk-HTLTCCFA.js +73 -0
- package/dist/chunk-HTLTCCFA.js.map +1 -0
- package/dist/chunk-I53W3CFN.js +56 -0
- package/dist/chunk-I53W3CFN.js.map +1 -0
- package/dist/chunk-IJVFPUXO.js +2 -0
- package/dist/chunk-IJVFPUXO.js.map +1 -0
- package/dist/chunk-JUPMOISE.js +51 -0
- package/dist/chunk-JUPMOISE.js.map +1 -0
- package/dist/chunk-K5RPSVO6.js +167 -0
- package/dist/chunk-K5RPSVO6.js.map +1 -0
- package/dist/chunk-K6U27YJ6.js +78 -0
- package/dist/chunk-K6U27YJ6.js.map +1 -0
- package/dist/chunk-KDVMGMAK.js +2 -0
- package/dist/chunk-KDVMGMAK.js.map +1 -0
- package/dist/chunk-LQOZCOK3.js +74 -0
- package/dist/chunk-LQOZCOK3.js.map +1 -0
- package/dist/chunk-NAQWNX2A.js +102 -0
- package/dist/chunk-NAQWNX2A.js.map +1 -0
- package/dist/chunk-NVJVWGUS.js +152 -0
- package/dist/chunk-NVJVWGUS.js.map +1 -0
- package/dist/chunk-O2F3R3PV.js +2 -0
- package/dist/chunk-O2F3R3PV.js.map +1 -0
- package/dist/chunk-OMFQHMA6.js +2 -0
- package/dist/chunk-OMFQHMA6.js.map +1 -0
- package/dist/chunk-PE6JXVOX.js +2 -0
- package/dist/chunk-PE6JXVOX.js.map +1 -0
- package/dist/chunk-ROJEIIA4.js +52 -0
- package/dist/chunk-ROJEIIA4.js.map +1 -0
- package/dist/chunk-ROYAQKJA.js +9 -0
- package/dist/chunk-ROYAQKJA.js.map +1 -0
- package/dist/chunk-SHCZK4DO.js +45 -0
- package/dist/chunk-SHCZK4DO.js.map +1 -0
- package/dist/chunk-SXXJV6YJ.js +79 -0
- package/dist/chunk-SXXJV6YJ.js.map +1 -0
- package/dist/chunk-T7S7NJFD.js +287 -0
- package/dist/chunk-T7S7NJFD.js.map +1 -0
- package/dist/chunk-TEZ3OXAY.js +63 -0
- package/dist/chunk-TEZ3OXAY.js.map +1 -0
- package/dist/chunk-TGWPM2CG.js +199 -0
- package/dist/chunk-TGWPM2CG.js.map +1 -0
- package/dist/chunk-TSB6RE7L.js +2 -0
- package/dist/chunk-TSB6RE7L.js.map +1 -0
- package/dist/chunk-UPV2MT4O.js +2 -0
- package/dist/chunk-UPV2MT4O.js.map +1 -0
- package/dist/chunk-UZJZY4A5.js +2 -0
- package/dist/chunk-UZJZY4A5.js.map +1 -0
- package/dist/chunk-W3TQMUGX.js +2 -0
- package/dist/chunk-W3TQMUGX.js.map +1 -0
- package/dist/chunk-WN46RIOC.js +29 -0
- package/dist/chunk-WN46RIOC.js.map +1 -0
- package/dist/chunk-XZFE2YYH.js +70 -0
- package/dist/chunk-XZFE2YYH.js.map +1 -0
- package/dist/chunk-YG3KXTZW.js +2 -0
- package/dist/chunk-YG3KXTZW.js.map +1 -0
- package/dist/chunk-ZATVF42E.js +65 -0
- package/dist/chunk-ZATVF42E.js.map +1 -0
- package/dist/chunk-ZXZTLQIX.js +19 -0
- package/dist/chunk-ZXZTLQIX.js.map +1 -0
- package/dist/flow-button-edge/flow-button-edge.js +10 -0
- package/dist/flow-button-edge/flow-button-edge.js.map +1 -0
- package/dist/flow-button-edge/index.js +11 -0
- package/dist/flow-button-edge/index.js.map +1 -0
- package/dist/flow-edge/flow-edge.js +10 -0
- package/dist/flow-edge/flow-edge.js.map +1 -0
- package/dist/flow-edge/index.js +11 -0
- package/dist/flow-edge/index.js.map +1 -0
- package/dist/flow-edge-path/flow-edge-path.js +12 -0
- package/dist/flow-edge-path/flow-edge-path.js.map +1 -0
- package/dist/flow-edge-path/index.js +13 -0
- package/dist/flow-edge-path/index.js.map +1 -0
- package/dist/flow-edge-tokens/edge-path-sampler.js +10 -0
- package/dist/flow-edge-tokens/edge-path-sampler.js.map +1 -0
- package/dist/flow-edge-tokens/flow-edge-tokens.js +12 -0
- package/dist/flow-edge-tokens/flow-edge-tokens.js.map +1 -0
- package/dist/flow-edge-tokens/index.js +17 -0
- package/dist/flow-edge-tokens/index.js.map +1 -0
- package/dist/flow-floating-edge/floating-edge-geometry.js +8 -0
- package/dist/flow-floating-edge/floating-edge-geometry.js.map +1 -0
- package/dist/flow-floating-edge/flow-floating-edge.js +11 -0
- package/dist/flow-floating-edge/flow-floating-edge.js.map +1 -0
- package/dist/flow-floating-edge/index.js +15 -0
- package/dist/flow-floating-edge/index.js.map +1 -0
- package/dist/flow-group-node/flow-group-node.js +12 -0
- package/dist/flow-group-node/flow-group-node.js.map +1 -0
- package/dist/flow-group-node/index.js +13 -0
- package/dist/flow-group-node/index.js.map +1 -0
- package/dist/flow-handle/flow-handle-anchor.js +8 -0
- package/dist/flow-handle/flow-handle-anchor.js.map +1 -0
- package/dist/flow-handle/index.js +9 -0
- package/dist/flow-handle/index.js.map +1 -0
- package/dist/flow-layout/backbone.js +8 -0
- package/dist/flow-layout/backbone.js.map +1 -0
- package/dist/flow-layout/flow-layout.js +10 -0
- package/dist/flow-layout/flow-layout.js.map +1 -0
- package/dist/flow-layout/index.js +21 -0
- package/dist/flow-layout/index.js.map +1 -0
- package/dist/flow-layout/layout-flow-elk.js +9 -0
- package/dist/flow-layout/layout-flow-elk.js.map +1 -0
- package/dist/flow-layout/layout-graph.js +9 -0
- package/dist/flow-layout/layout-graph.js.map +1 -0
- package/dist/flow-layout/use-auto-layout.js +10 -0
- package/dist/flow-layout/use-auto-layout.js.map +1 -0
- package/dist/flow-layout/use-flow-layout.js +9 -0
- package/dist/flow-layout/use-flow-layout.js.map +1 -0
- package/dist/flow-mini-map/flow-mini-map.js +8 -0
- package/dist/flow-mini-map/flow-mini-map.js.map +1 -0
- package/dist/flow-mini-map/index.js +9 -0
- package/dist/flow-mini-map/index.js.map +1 -0
- package/dist/flow-node/flow-node.js +11 -0
- package/dist/flow-node/flow-node.js.map +1 -0
- package/dist/flow-node/index.js +12 -0
- package/dist/flow-node/index.js.map +1 -0
- package/dist/flow-placeholder-node/flow-placeholder-node.js +9 -0
- package/dist/flow-placeholder-node/flow-placeholder-node.js.map +1 -0
- package/dist/flow-placeholder-node/index.js +10 -0
- package/dist/flow-placeholder-node/index.js.map +1 -0
- package/dist/flow-self-loop-edge/flow-self-loop-edge.js +20 -0
- package/dist/flow-self-loop-edge/flow-self-loop-edge.js.map +1 -0
- package/dist/flow-self-loop-edge/index.js +28 -0
- package/dist/flow-self-loop-edge/index.js.map +1 -0
- package/dist/flow-self-loop-edge/self-loop-geometry.js +12 -0
- package/dist/flow-self-loop-edge/self-loop-geometry.js.map +1 -0
- package/dist/flow-smart-edge/flow-smart-edge.js +11 -0
- package/dist/flow-smart-edge/flow-smart-edge.js.map +1 -0
- package/dist/flow-smart-edge/index.js +25 -0
- package/dist/flow-smart-edge/index.js.map +1 -0
- package/dist/flow-smart-edge/smart-edge-geometry.js +18 -0
- package/dist/flow-smart-edge/smart-edge-geometry.js.map +1 -0
- package/dist/flow-weighted-edge/back-edge-geometry.js +8 -0
- package/dist/flow-weighted-edge/back-edge-geometry.js.map +1 -0
- package/dist/flow-weighted-edge/edge-aria.js +10 -0
- package/dist/flow-weighted-edge/edge-aria.js.map +1 -0
- package/dist/flow-weighted-edge/edge-label-pill.js +8 -0
- package/dist/flow-weighted-edge/edge-label-pill.js.map +1 -0
- package/dist/flow-weighted-edge/flow-weighted-edge.js +16 -0
- package/dist/flow-weighted-edge/flow-weighted-edge.js.map +1 -0
- package/dist/flow-weighted-edge/index.js +34 -0
- package/dist/flow-weighted-edge/index.js.map +1 -0
- package/dist/flow-weighted-edge/weight-scale.js +10 -0
- package/dist/flow-weighted-edge/weight-scale.js.map +1 -0
- package/dist/helper-lines/get-helper-lines.js +8 -0
- package/dist/helper-lines/get-helper-lines.js.map +1 -0
- package/dist/helper-lines/helper-lines.js +9 -0
- package/dist/helper-lines/helper-lines.js.map +1 -0
- package/dist/helper-lines/index.js +17 -0
- package/dist/helper-lines/index.js.map +1 -0
- package/dist/helper-lines/use-helper-lines.js +10 -0
- package/dist/helper-lines/use-helper-lines.js.map +1 -0
- package/dist/index.d.ts +236 -1
- package/dist/index.js +167 -2311
- package/dist/index.js.map +1 -1
- package/dist/inspector-panel/index.js +9 -0
- package/dist/inspector-panel/index.js.map +1 -0
- package/dist/inspector-panel/inspector-panel.js +8 -0
- package/dist/inspector-panel/inspector-panel.js.map +1 -0
- package/dist/legend/index.js +10 -0
- package/dist/legend/index.js.map +1 -0
- package/dist/legend/legend.js +9 -0
- package/dist/legend/legend.js.map +1 -0
- package/dist/use-flow-groups/group-operations.js +20 -0
- package/dist/use-flow-groups/group-operations.js.map +1 -0
- package/dist/use-flow-groups/index.js +25 -0
- package/dist/use-flow-groups/index.js.map +1 -0
- package/dist/use-flow-groups/use-flow-groups.js +9 -0
- package/dist/use-flow-groups/use-flow-groups.js.map +1 -0
- package/dist/zoom-controls/index.js +9 -0
- package/dist/zoom-controls/index.js.map +1 -0
- package/dist/zoom-controls/zoom-controls.js +8 -0
- package/dist/zoom-controls/zoom-controls.js.map +1 -0
- package/package.json +13 -6
- package/src/canvas-shell/canvas-shell.stories.tsx +1 -0
- package/src/canvas-shell/canvas-shell.tsx +51 -4
- package/src/canvas-shell/clamped-fit-offset.test.ts +25 -0
- package/src/canvas-shell/clamped-fit-offset.ts +30 -0
- package/src/custom-nodes/custom-nodes.stories.tsx +718 -0
- package/src/flow-edge-tokens/edge-path-sampler.test.ts +108 -0
- package/src/flow-edge-tokens/edge-path-sampler.ts +362 -0
- package/src/flow-edge-tokens/flow-edge-tokens.stories.tsx +312 -0
- package/src/flow-edge-tokens/flow-edge-tokens.test.tsx +115 -0
- package/src/flow-edge-tokens/flow-edge-tokens.tsx +94 -0
- package/src/flow-edge-tokens/index.ts +12 -0
- package/src/flow-layout/backbone.test.ts +88 -0
- package/src/flow-layout/backbone.ts +58 -0
- package/src/flow-layout/layout-flow-elk.stories.tsx +154 -0
- package/src/flow-layout/layout-flow-elk.test.ts +152 -0
- package/src/flow-layout/layout-flow-elk.ts +399 -0
- package/src/flow-mini-map/flow-mini-map.stories.tsx +10 -7
- package/src/flow-node/flow-node.stories.tsx +40 -1
- package/src/flow-self-loop-edge/flow-self-loop-edge.test.tsx +38 -0
- package/src/flow-self-loop-edge/flow-self-loop-edge.tsx +7 -0
- package/src/flow-weighted-edge/edge-label-pill.test.tsx +20 -0
- package/src/flow-weighted-edge/flow-weighted-edge.stories.tsx +1 -0
- package/src/flow-weighted-edge/flow-weighted-edge.test.tsx +40 -0
- package/src/flow-weighted-edge/flow-weighted-edge.tsx +7 -0
- package/src/index.ts +13 -0
- package/src/templates-flow-workspace.stories.tsx +7 -2
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/flow-self-loop-edge/self-loop-geometry.ts
|
|
4
|
+
var DEFAULT_LOOP_RADIUS = 28;
|
|
5
|
+
var CONTROL_REACH_X = 1.2;
|
|
6
|
+
var CONTROL_REACH_Y = 2.4;
|
|
7
|
+
var APEX_FACTOR = 3 / 4 * CONTROL_REACH_Y;
|
|
8
|
+
function selfLoopPath(anchor, loopRadius) {
|
|
9
|
+
const cx = Number.isFinite(anchor.centerX) ? anchor.centerX : 0;
|
|
10
|
+
const ay = Number.isFinite(anchor.topY) ? anchor.topY : 0;
|
|
11
|
+
const r = Number.isFinite(loopRadius) && loopRadius > 0 ? loopRadius : DEFAULT_LOOP_RADIUS;
|
|
12
|
+
const startX = cx + r;
|
|
13
|
+
const endX = cx - r;
|
|
14
|
+
const controlY = ay - r * CONTROL_REACH_Y;
|
|
15
|
+
return {
|
|
16
|
+
path: `M ${startX},${ay} C ${startX + r * CONTROL_REACH_X},${controlY} ${endX - r * CONTROL_REACH_X},${controlY} ${endX},${ay}`,
|
|
17
|
+
labelX: cx,
|
|
18
|
+
labelY: ay - r * APEX_FACTOR
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function outward(x, y, centerX, centerY, fallback) {
|
|
22
|
+
const dx = x - centerX;
|
|
23
|
+
const dy = y - centerY;
|
|
24
|
+
const length = Math.hypot(dx, dy);
|
|
25
|
+
if (!Number.isFinite(length) || length < 1e-6) return fallback;
|
|
26
|
+
return [dx / length, dy / length];
|
|
27
|
+
}
|
|
28
|
+
function selfLoopHandleArc(anchor, loopRadius) {
|
|
29
|
+
const num = (value) => Number.isFinite(value) ? value : 0;
|
|
30
|
+
const sx = num(anchor.sourceX);
|
|
31
|
+
const sy = num(anchor.sourceY);
|
|
32
|
+
const tx = num(anchor.targetX);
|
|
33
|
+
const ty = num(anchor.targetY);
|
|
34
|
+
const cx = num(anchor.centerX);
|
|
35
|
+
const cy = num(anchor.centerY);
|
|
36
|
+
const width = Math.max(0, num(anchor.width));
|
|
37
|
+
const height = Math.max(0, num(anchor.height));
|
|
38
|
+
const r = Number.isFinite(loopRadius) && loopRadius > 0 ? loopRadius : DEFAULT_LOOP_RADIUS;
|
|
39
|
+
const [osx, osy] = outward(sx, sy, cx, cy, [0, 1]);
|
|
40
|
+
const [otx, oty] = outward(tx, ty, cx, cy, [-osx, -osy]);
|
|
41
|
+
const lx = osy;
|
|
42
|
+
const ly = -osx;
|
|
43
|
+
const clearance = Math.abs(lx) * (width / 2) + Math.abs(ly) * (height / 2) + r;
|
|
44
|
+
const reach = 4 / 3 * clearance;
|
|
45
|
+
const c1x = sx + osx * r + lx * reach;
|
|
46
|
+
const c1y = sy + osy * r + ly * reach;
|
|
47
|
+
const c2x = tx + otx * r + lx * reach;
|
|
48
|
+
const c2y = ty + oty * r + ly * reach;
|
|
49
|
+
return {
|
|
50
|
+
path: `M ${sx},${sy} C ${c1x},${c1y} ${c2x},${c2y} ${tx},${ty}`,
|
|
51
|
+
// A cubic's midpoint is (P0 + 3·C1 + 3·C2 + P3) / 8 — the arc's apex, which `reach`
|
|
52
|
+
// above places `loopRadius` clear of the card, so the label sits off the node.
|
|
53
|
+
labelX: (sx + 3 * c1x + 3 * c2x + tx) / 8,
|
|
54
|
+
labelY: (sy + 3 * c1y + 3 * c2y + ty) / 8
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
DEFAULT_LOOP_RADIUS,
|
|
60
|
+
selfLoopPath,
|
|
61
|
+
selfLoopHandleArc
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=chunk-TEZ3OXAY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/flow-self-loop-edge/self-loop-geometry.ts"],"sourcesContent":["/**\n * self-loop-geometry — pure, framework-free arc math for a self-referencing edge.\n *\n * Kept out of the component (and out of React) so the loop's shape can be\n * unit-tested without a canvas, and so a sibling that needs the same apex\n * point (a legend, an overlay, a screenshot harness) can compute it directly.\n */\n\n/** Default arc radius, in px. A loop this size clears `FlowNode`'s header without dominating it. */\nexport const DEFAULT_LOOP_RADIUS = 28;\n\n/**\n * How far the cubic's control points reach sideways and upwards, as multiples\n * of `loopRadius`. Tuned so the arc reads as a closed loop rather than a bump:\n * the horizontal reach opens the throat of the loop, the vertical reach sets\n * its height.\n */\nconst CONTROL_REACH_X = 1.2;\nconst CONTROL_REACH_Y = 2.4;\n/** A symmetric cubic's midpoint sits at 3/4 of its control-point height — see `selfLoopPath`. */\nconst APEX_FACTOR = (3 / 4) * CONTROL_REACH_Y;\n\n/** The box a self-loop is drawn above. Matches a React Flow `InternalNode` structurally. */\nexport interface SelfLoopAnchor {\n /** Horizontal centre of the node the loop belongs to. */\n centerX: number;\n /** Top edge of that node — the loop is drawn above this line. */\n topY: number;\n}\n\nexport interface SelfLoopPath {\n /** SVG `d` for the arc. */\n path: string;\n /** Where a label belongs: the arc's apex. */\n labelX: number;\n labelY: number;\n}\n\n/**\n * A cubic arc that leaves the node's top-RIGHT, bulges up over the node, and\n * re-enters at its top-LEFT — the conventional \"this step repeated\" mark in a\n * process map, and a SHAPE rather than a colour, so it survives greyscale.\n *\n * The curve is symmetric about `centerX`, which puts its `t = 0.5` midpoint\n * exactly at `(centerX, topY - APEX_FACTOR × loopRadius)` — the apex the label\n * is anchored to. Never returns `NaN`: a non-finite input falls back to `0`\n * and a non-positive radius falls back to the default.\n */\nexport function selfLoopPath(anchor: SelfLoopAnchor, loopRadius: number): SelfLoopPath {\n const cx = Number.isFinite(anchor.centerX) ? anchor.centerX : 0;\n const ay = Number.isFinite(anchor.topY) ? anchor.topY : 0;\n const r = Number.isFinite(loopRadius) && loopRadius > 0 ? loopRadius : DEFAULT_LOOP_RADIUS;\n\n const startX = cx + r;\n const endX = cx - r;\n const controlY = ay - r * CONTROL_REACH_Y;\n\n return {\n path: `M ${startX},${ay} C ${startX + r * CONTROL_REACH_X},${controlY} ${\n endX - r * CONTROL_REACH_X\n },${controlY} ${endX},${ay}`,\n labelX: cx,\n labelY: ay - r * APEX_FACTOR,\n };\n}\n\n/**\n * The two handle anchors a self-loop actually joins, plus the box it has to clear.\n *\n * A self-loop's `sourceX/sourceY` and `targetX/targetY` are the SAME node's two handle\n * points, which is why they describe no useful straight line — but they are still the\n * only two points on the canvas the reader recognises as connectors, so the arc has to\n * start and end exactly on them. {@link selfLoopHandleArc} is the geometry that does\n * that; {@link selfLoopPath} remains the node-box arc used before a node is measured.\n */\nexport interface SelfLoopHandleAnchor {\n /** Where the loop leaves the node — React Flow's own SOURCE handle anchor. */\n sourceX: number;\n sourceY: number;\n /** Where it re-enters — React Flow's own TARGET handle anchor. */\n targetX: number;\n targetY: number;\n /** The node's centre. Decides which way is \"out of the card\". */\n centerX: number;\n centerY: number;\n /** The node's rendered size, so the arc clears the card instead of crossing it. */\n width: number;\n height: number;\n}\n\n/** Unit vector from the node's centre to a handle point, or `fallback` when degenerate. */\nfunction outward(\n x: number,\n y: number,\n centerX: number,\n centerY: number,\n fallback: readonly [number, number],\n): readonly [number, number] {\n const dx = x - centerX;\n const dy = y - centerY;\n const length = Math.hypot(dx, dy);\n if (!Number.isFinite(length) || length < 1e-6) return fallback;\n return [dx / length, dy / length];\n}\n\n/**\n * A cubic that leaves the SOURCE handle dot, bulges clear of the node on one side, and\n * re-enters at the TARGET handle dot.\n *\n * It is direction-agnostic by construction: the bulge is the source normal rotated a\n * quarter turn, so a top-to-bottom layout (handles on the bottom and top) gets a lasso\n * down the node's right-hand side, and a left-to-right layout (handles on the right and\n * left) gets an arc over the top — with no `direction` prop, and no list of cases to keep\n * in step with `layoutFlow`'s `HANDLE_BY_DIRECTION`.\n *\n * `loopRadius` is how far the curve shoots straight out of each dot before it turns, and\n * also the gap the arc's widest point keeps from the card's edge. The label sits at that\n * widest point — the cubic's `t = 0.5` midpoint — so it is off the node by construction.\n *\n * Never returns `NaN`: non-finite inputs fall back to `0`, a non-positive radius to\n * {@link DEFAULT_LOOP_RADIUS}, and a handle point sitting on the node's centre (nothing\n * measured yet) to a downward source normal.\n */\nexport function selfLoopHandleArc(anchor: SelfLoopHandleAnchor, loopRadius: number): SelfLoopPath {\n const num = (value: number) => (Number.isFinite(value) ? value : 0);\n const sx = num(anchor.sourceX);\n const sy = num(anchor.sourceY);\n const tx = num(anchor.targetX);\n const ty = num(anchor.targetY);\n const cx = num(anchor.centerX);\n const cy = num(anchor.centerY);\n const width = Math.max(0, num(anchor.width));\n const height = Math.max(0, num(anchor.height));\n const r = Number.isFinite(loopRadius) && loopRadius > 0 ? loopRadius : DEFAULT_LOOP_RADIUS;\n\n const [osx, osy] = outward(sx, sy, cx, cy, [0, 1]);\n const [otx, oty] = outward(tx, ty, cx, cy, [-osx, -osy]);\n // A quarter turn from the source normal: the side the loop bulges out on.\n const lx = osy;\n const ly = -osx;\n // How far the card extends along the bulge, plus the clearance we want beyond it.\n const clearance = Math.abs(lx) * (width / 2) + Math.abs(ly) * (height / 2) + r;\n // A cubic reaches only 3/4 of the way to its control points at the midpoint — its\n // WIDEST point — so `clearance` has to be divided by that factor, not used directly.\n // Used directly (the first cut of this function did) the apex lands at 0.75 × clearance,\n // which for a 176 px card is 87 px against an 88 px half-width: the loop is drawn ON the\n // card it is supposed to encircle, and its label — anchored at that same midpoint —\n // prints on top of the activity's own name. Measured on the process map: one collision\n // per direction that no amount of layout spacing could remove, because the arc's reach\n // is a property of the node it belongs to, not of the gap to its neighbours.\n const reach = (4 / 3) * clearance;\n\n const c1x = sx + osx * r + lx * reach;\n const c1y = sy + osy * r + ly * reach;\n const c2x = tx + otx * r + lx * reach;\n const c2y = ty + oty * r + ly * reach;\n\n return {\n path: `M ${sx},${sy} C ${c1x},${c1y} ${c2x},${c2y} ${tx},${ty}`,\n // A cubic's midpoint is (P0 + 3·C1 + 3·C2 + P3) / 8 — the arc's apex, which `reach`\n // above places `loopRadius` clear of the card, so the label sits off the node.\n labelX: (sx + 3 * c1x + 3 * c2x + tx) / 8,\n labelY: (sy + 3 * c1y + 3 * c2y + ty) / 8,\n };\n}\n"],"mappings":";;;AASO,IAAM,sBAAsB;AAQnC,IAAM,kBAAkB;AACxB,IAAM,kBAAkB;AAExB,IAAM,cAAe,IAAI,IAAK;AA4BvB,SAAS,aAAa,QAAwB,YAAkC;AACrF,QAAM,KAAK,OAAO,SAAS,OAAO,OAAO,IAAI,OAAO,UAAU;AAC9D,QAAM,KAAK,OAAO,SAAS,OAAO,IAAI,IAAI,OAAO,OAAO;AACxD,QAAM,IAAI,OAAO,SAAS,UAAU,KAAK,aAAa,IAAI,aAAa;AAEvE,QAAM,SAAS,KAAK;AACpB,QAAM,OAAO,KAAK;AAClB,QAAM,WAAW,KAAK,IAAI;AAE1B,SAAO;AAAA,IACL,MAAM,KAAK,MAAM,IAAI,EAAE,MAAM,SAAS,IAAI,eAAe,IAAI,QAAQ,IACnE,OAAO,IAAI,eACb,IAAI,QAAQ,IAAI,IAAI,IAAI,EAAE;AAAA,IAC1B,QAAQ;AAAA,IACR,QAAQ,KAAK,IAAI;AAAA,EACnB;AACF;AA2BA,SAAS,QACP,GACA,GACA,SACA,SACA,UAC2B;AAC3B,QAAM,KAAK,IAAI;AACf,QAAM,KAAK,IAAI;AACf,QAAM,SAAS,KAAK,MAAM,IAAI,EAAE;AAChC,MAAI,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,KAAM,QAAO;AACtD,SAAO,CAAC,KAAK,QAAQ,KAAK,MAAM;AAClC;AAoBO,SAAS,kBAAkB,QAA8B,YAAkC;AAChG,QAAM,MAAM,CAAC,UAAmB,OAAO,SAAS,KAAK,IAAI,QAAQ;AACjE,QAAM,KAAK,IAAI,OAAO,OAAO;AAC7B,QAAM,KAAK,IAAI,OAAO,OAAO;AAC7B,QAAM,KAAK,IAAI,OAAO,OAAO;AAC7B,QAAM,KAAK,IAAI,OAAO,OAAO;AAC7B,QAAM,KAAK,IAAI,OAAO,OAAO;AAC7B,QAAM,KAAK,IAAI,OAAO,OAAO;AAC7B,QAAM,QAAQ,KAAK,IAAI,GAAG,IAAI,OAAO,KAAK,CAAC;AAC3C,QAAM,SAAS,KAAK,IAAI,GAAG,IAAI,OAAO,MAAM,CAAC;AAC7C,QAAM,IAAI,OAAO,SAAS,UAAU,KAAK,aAAa,IAAI,aAAa;AAEvE,QAAM,CAAC,KAAK,GAAG,IAAI,QAAQ,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;AACjD,QAAM,CAAC,KAAK,GAAG,IAAI,QAAQ,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;AAEvD,QAAM,KAAK;AACX,QAAM,KAAK,CAAC;AAEZ,QAAM,YAAY,KAAK,IAAI,EAAE,KAAK,QAAQ,KAAK,KAAK,IAAI,EAAE,KAAK,SAAS,KAAK;AAS7E,QAAM,QAAS,IAAI,IAAK;AAExB,QAAM,MAAM,KAAK,MAAM,IAAI,KAAK;AAChC,QAAM,MAAM,KAAK,MAAM,IAAI,KAAK;AAChC,QAAM,MAAM,KAAK,MAAM,IAAI,KAAK;AAChC,QAAM,MAAM,KAAK,MAAM,IAAI,KAAK;AAEhC,SAAO;AAAA,IACL,MAAM,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,IAAI,EAAE;AAAA;AAAA;AAAA,IAG7D,SAAS,KAAK,IAAI,MAAM,IAAI,MAAM,MAAM;AAAA,IACxC,SAAS,KAAK,IAAI,MAAM,IAAI,MAAM,MAAM;AAAA,EAC1C;AACF;","names":[]}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/use-flow-groups/group-operations.ts
|
|
4
|
+
import { getNodesBounds } from "@xyflow/react";
|
|
5
|
+
var FLOW_GROUP_NODE_TYPE = "group";
|
|
6
|
+
var DEFAULT_GROUP_PADDING = 28;
|
|
7
|
+
var GROUP_HEADER_OFFSET = 44;
|
|
8
|
+
var OVERVIEW_WIDTH = 220;
|
|
9
|
+
var OVERVIEW_HEIGHT = 48;
|
|
10
|
+
var COLLAPSED_STATE_KEY = "__flowGroupCollapsedState";
|
|
11
|
+
function isFlowGroupProxyEdge(edge) {
|
|
12
|
+
return Boolean(edge.data?.__flowGroupProxy);
|
|
13
|
+
}
|
|
14
|
+
function proxyEdgeId(groupId, originalEdgeId) {
|
|
15
|
+
return `flow-group-proxy__${groupId}__${originalEdgeId}`;
|
|
16
|
+
}
|
|
17
|
+
function readCollapsedState(node) {
|
|
18
|
+
return node.data?.[COLLAPSED_STATE_KEY];
|
|
19
|
+
}
|
|
20
|
+
function collectDescendantIds(nodes, groupId) {
|
|
21
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
22
|
+
for (const n of nodes) {
|
|
23
|
+
if (n.parentId) {
|
|
24
|
+
const arr = childrenByParent.get(n.parentId);
|
|
25
|
+
if (arr) arr.push(n);
|
|
26
|
+
else childrenByParent.set(n.parentId, [n]);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const out = /* @__PURE__ */ new Set();
|
|
30
|
+
const stack = [...childrenByParent.get(groupId) ?? []];
|
|
31
|
+
while (stack.length > 0) {
|
|
32
|
+
const n = stack.pop();
|
|
33
|
+
if (out.has(n.id)) continue;
|
|
34
|
+
out.add(n.id);
|
|
35
|
+
const kids = childrenByParent.get(n.id);
|
|
36
|
+
if (kids) stack.push(...kids);
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
function groupNodes(nodes, edges, nodeIds, options) {
|
|
41
|
+
const idSet = new Set(nodeIds);
|
|
42
|
+
const children = nodes.filter((n) => idSet.has(n.id));
|
|
43
|
+
if (children.length === 0) return { nodes, edges };
|
|
44
|
+
const bounds = getNodesBounds(children);
|
|
45
|
+
const padding = options.padding ?? DEFAULT_GROUP_PADDING;
|
|
46
|
+
const originX = bounds.x - padding;
|
|
47
|
+
const originY = bounds.y - padding - GROUP_HEADER_OFFSET;
|
|
48
|
+
const data = {
|
|
49
|
+
title: options.title ?? "Group",
|
|
50
|
+
childCount: children.length,
|
|
51
|
+
...options.tone ? { tone: options.tone } : {}
|
|
52
|
+
};
|
|
53
|
+
const groupNode = {
|
|
54
|
+
id: options.groupId,
|
|
55
|
+
type: FLOW_GROUP_NODE_TYPE,
|
|
56
|
+
position: { x: originX, y: originY },
|
|
57
|
+
width: bounds.width + padding * 2,
|
|
58
|
+
height: bounds.height + padding * 2 + GROUP_HEADER_OFFSET,
|
|
59
|
+
data
|
|
60
|
+
};
|
|
61
|
+
const reparented = /* @__PURE__ */ new Map();
|
|
62
|
+
for (const child of children) {
|
|
63
|
+
reparented.set(child.id, {
|
|
64
|
+
...child,
|
|
65
|
+
parentId: options.groupId,
|
|
66
|
+
extent: "parent",
|
|
67
|
+
position: { x: child.position.x - originX, y: child.position.y - originY }
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
let earliest = nodes.length;
|
|
71
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
72
|
+
if (idSet.has(nodes[i].id)) {
|
|
73
|
+
earliest = i;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const mapped = nodes.map((n) => reparented.get(n.id) ?? n);
|
|
78
|
+
const resultNodes = [...mapped.slice(0, earliest), groupNode, ...mapped.slice(earliest)];
|
|
79
|
+
return { nodes: resultNodes, edges };
|
|
80
|
+
}
|
|
81
|
+
function ungroup(nodes, edges, groupId) {
|
|
82
|
+
const group = nodes.find((n) => n.id === groupId);
|
|
83
|
+
if (!group) return { nodes, edges };
|
|
84
|
+
const base = group.data?.collapsed ? expandGroup(nodes, edges, groupId) : { nodes, edges };
|
|
85
|
+
const liveGroup = base.nodes.find((n) => n.id === groupId);
|
|
86
|
+
const gx = liveGroup.position.x;
|
|
87
|
+
const gy = liveGroup.position.y;
|
|
88
|
+
const resultNodes = base.nodes.filter((n) => n.id !== groupId).map((n) => {
|
|
89
|
+
if (n.parentId === groupId) {
|
|
90
|
+
const {
|
|
91
|
+
parentId: _parentId,
|
|
92
|
+
extent: _extent,
|
|
93
|
+
...rest
|
|
94
|
+
} = n;
|
|
95
|
+
return {
|
|
96
|
+
...rest,
|
|
97
|
+
position: { x: n.position.x + gx, y: n.position.y + gy }
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return n;
|
|
101
|
+
});
|
|
102
|
+
return { nodes: resultNodes, edges: base.edges };
|
|
103
|
+
}
|
|
104
|
+
function collapseGroup(nodes, edges, groupId) {
|
|
105
|
+
const group = nodes.find((n) => n.id === groupId);
|
|
106
|
+
if (!group) return { nodes, edges };
|
|
107
|
+
if (group.data?.collapsed) return { nodes, edges };
|
|
108
|
+
const descendantIds = collectDescendantIds(nodes, groupId);
|
|
109
|
+
const descendantSnapshots = nodes.filter((n) => descendantIds.has(n.id));
|
|
110
|
+
const directChildCount = nodes.filter((n) => n.parentId === groupId).length;
|
|
111
|
+
const isInside = (id) => descendantIds.has(id);
|
|
112
|
+
const reroutedEdges = [];
|
|
113
|
+
const proxyEdges = [];
|
|
114
|
+
for (const edge of edges) {
|
|
115
|
+
const sourceInside = isInside(edge.source);
|
|
116
|
+
const targetInside = isInside(edge.target);
|
|
117
|
+
if (sourceInside === targetInside) continue;
|
|
118
|
+
reroutedEdges.push(edge);
|
|
119
|
+
const proxyData = {
|
|
120
|
+
...edge.data ?? {},
|
|
121
|
+
__flowGroupProxy: true,
|
|
122
|
+
groupId
|
|
123
|
+
};
|
|
124
|
+
proxyEdges.push({
|
|
125
|
+
...edge,
|
|
126
|
+
id: proxyEdgeId(groupId, edge.id),
|
|
127
|
+
source: sourceInside ? groupId : edge.source,
|
|
128
|
+
target: targetInside ? groupId : edge.target,
|
|
129
|
+
sourceHandle: void 0,
|
|
130
|
+
targetHandle: void 0,
|
|
131
|
+
data: proxyData
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
const collapsedState = {
|
|
135
|
+
groupSnapshot: group,
|
|
136
|
+
descendantSnapshots,
|
|
137
|
+
reroutedEdges
|
|
138
|
+
};
|
|
139
|
+
const resultNodes = nodes.map((n) => {
|
|
140
|
+
if (n.id === groupId) {
|
|
141
|
+
return {
|
|
142
|
+
...n,
|
|
143
|
+
width: OVERVIEW_WIDTH,
|
|
144
|
+
height: OVERVIEW_HEIGHT,
|
|
145
|
+
data: {
|
|
146
|
+
...n.data,
|
|
147
|
+
collapsed: true,
|
|
148
|
+
childCount: directChildCount,
|
|
149
|
+
[COLLAPSED_STATE_KEY]: collapsedState
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (descendantIds.has(n.id)) {
|
|
154
|
+
return { ...n, hidden: true };
|
|
155
|
+
}
|
|
156
|
+
return n;
|
|
157
|
+
});
|
|
158
|
+
const reroutedIds = new Set(reroutedEdges.map((e) => e.id));
|
|
159
|
+
const resultEdges = edges.map(
|
|
160
|
+
(e) => reroutedIds.has(e.id) ? { ...e, hidden: true } : e
|
|
161
|
+
);
|
|
162
|
+
resultEdges.push(...proxyEdges);
|
|
163
|
+
return { nodes: resultNodes, edges: resultEdges };
|
|
164
|
+
}
|
|
165
|
+
function expandGroup(nodes, edges, groupId) {
|
|
166
|
+
const group = nodes.find((n) => n.id === groupId);
|
|
167
|
+
const state = group ? readCollapsedState(group) : void 0;
|
|
168
|
+
if (!group || !state) return { nodes, edges };
|
|
169
|
+
const snapshotById = new Map(state.descendantSnapshots.map((n) => [n.id, n]));
|
|
170
|
+
const reroutedById = new Map(state.reroutedEdges.map((e) => [e.id, e]));
|
|
171
|
+
const resultNodes = nodes.map((n) => {
|
|
172
|
+
if (n.id === groupId) return state.groupSnapshot;
|
|
173
|
+
const snap = snapshotById.get(n.id);
|
|
174
|
+
return snap ? snap : n;
|
|
175
|
+
});
|
|
176
|
+
const resultEdges = edges.filter(
|
|
177
|
+
(e) => !(isFlowGroupProxyEdge(e) && e.data?.groupId === groupId)
|
|
178
|
+
).map((e) => {
|
|
179
|
+
const original = reroutedById.get(e.id);
|
|
180
|
+
return original ? original : e;
|
|
181
|
+
});
|
|
182
|
+
return { nodes: resultNodes, edges: resultEdges };
|
|
183
|
+
}
|
|
184
|
+
function toggleGroupCollapsed(nodes, edges, groupId) {
|
|
185
|
+
const group = nodes.find((n) => n.id === groupId);
|
|
186
|
+
if (!group) return { nodes, edges };
|
|
187
|
+
return group.data?.collapsed ? expandGroup(nodes, edges, groupId) : collapseGroup(nodes, edges, groupId);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export {
|
|
191
|
+
FLOW_GROUP_NODE_TYPE,
|
|
192
|
+
isFlowGroupProxyEdge,
|
|
193
|
+
groupNodes,
|
|
194
|
+
ungroup,
|
|
195
|
+
collapseGroup,
|
|
196
|
+
expandGroup,
|
|
197
|
+
toggleGroupCollapsed
|
|
198
|
+
};
|
|
199
|
+
//# sourceMappingURL=chunk-TGWPM2CG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/use-flow-groups/group-operations.ts"],"sourcesContent":["import { getNodesBounds, type Edge, type Node } from \"@xyflow/react\";\nimport type { FlowGroupNodeData, FlowGroupTone } from \"../flow-group-node\";\n\n/**\n * Pure, framework-agnostic grouping operations over `nodes`/`edges` arrays.\n *\n * Every function is a **pure transform** — it never mutates its inputs and\n * returns a fresh `{ nodes, edges }`. This is the testable core; `useFlowGroups`\n * is a thin hook that wires these to a live canvas via `useReactFlow()`.\n */\n\n/** Node type used for the group container (matches `nodeTypes={{ group: FlowGroupNode }}`). */\nexport const FLOW_GROUP_NODE_TYPE = \"group\";\n\n/** Padding (px) left around the child bounds when a group is created. */\nconst DEFAULT_GROUP_PADDING = 28;\n/** Extra top space reserved for the group header so children don't sit under it. */\nconst GROUP_HEADER_OFFSET = 44;\n/** Fixed size of the collapsed \"overview chip\". */\nconst OVERVIEW_WIDTH = 220;\nconst OVERVIEW_HEIGHT = 48;\n\nexport interface GroupNodesOptions {\n /** Id for the new group node. Required so the operation stays deterministic/testable. */\n groupId: string;\n /** Header title for the group. @default \"Group\" */\n title?: string;\n /** Accent tone for the group header. */\n tone?: FlowGroupTone;\n /** Padding (px) around the child bounds. @default 28 */\n padding?: number;\n}\n\nexport interface FlowGroupOperationResult<\n NodeType extends Node = Node,\n EdgeType extends Edge = Edge,\n> {\n nodes: NodeType[];\n edges: EdgeType[];\n}\n\n/**\n * Snapshot stashed on a collapsed group's `data` so `expandGroup` can restore the\n * exact pre-collapse graph (an exact inverse). Holds original node/edge object\n * references — never mutated — so restoration is object-exact.\n */\ninterface FlowGroupCollapsedState {\n /** The group node exactly as it was before collapse (no collapsed state on it). */\n groupSnapshot: Node;\n /** Every descendant node exactly as it was before collapse. */\n descendantSnapshots: Node[];\n /** The boundary-crossing edges exactly as they were before rerouting. */\n reroutedEdges: Edge[];\n}\n\nconst COLLAPSED_STATE_KEY = \"__flowGroupCollapsedState\";\n\n/** Marker + metadata written onto a proxy edge's `data`. */\nexport interface FlowGroupProxyEdgeData extends Record<string, unknown> {\n __flowGroupProxy: true;\n /** Id of the collapsed group this proxy edge stands in for. */\n groupId: string;\n}\n\n/** True if `edge` is a proxy edge synthesized for a collapsed group. */\nexport function isFlowGroupProxyEdge(edge: Edge): boolean {\n return Boolean((edge.data as Partial<FlowGroupProxyEdgeData> | undefined)?.__flowGroupProxy);\n}\n\nfunction proxyEdgeId(groupId: string, originalEdgeId: string): string {\n return `flow-group-proxy__${groupId}__${originalEdgeId}`;\n}\n\nfunction readCollapsedState(node: Node): FlowGroupCollapsedState | undefined {\n return (node.data as Record<string, unknown> | undefined)?.[COLLAPSED_STATE_KEY] as\n | FlowGroupCollapsedState\n | undefined;\n}\n\n/** All node ids whose parent chain reaches `groupId` (children, grandchildren, …). */\nfunction collectDescendantIds(nodes: Node[], groupId: string): Set<string> {\n const childrenByParent = new Map<string, Node[]>();\n for (const n of nodes) {\n if (n.parentId) {\n const arr = childrenByParent.get(n.parentId);\n if (arr) arr.push(n);\n else childrenByParent.set(n.parentId, [n]);\n }\n }\n\n const out = new Set<string>();\n const stack = [...(childrenByParent.get(groupId) ?? [])];\n while (stack.length > 0) {\n const n = stack.pop()!;\n if (out.has(n.id)) continue;\n out.add(n.id);\n const kids = childrenByParent.get(n.id);\n if (kids) stack.push(...kids);\n }\n return out;\n}\n\n/**\n * Wrap `nodeIds` (assumed top-level) in a new `FlowGroupNode` parent.\n *\n * - Computes the child bounds with `getNodesBounds`.\n * - Inserts the group node **before** its children in the array (React Flow\n * requires a parent to precede its children).\n * - Re-parents each child (`parentId`, `extent: \"parent\"`) and rewrites its\n * position to be **relative to the group origin**.\n */\nexport function groupNodes<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n nodeIds: string[],\n options: GroupNodesOptions,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const idSet = new Set(nodeIds);\n const children = nodes.filter((n) => idSet.has(n.id));\n if (children.length === 0) return { nodes, edges };\n\n const bounds = getNodesBounds(children);\n const padding = options.padding ?? DEFAULT_GROUP_PADDING;\n const originX = bounds.x - padding;\n const originY = bounds.y - padding - GROUP_HEADER_OFFSET;\n\n const data: FlowGroupNodeData = {\n title: options.title ?? \"Group\",\n childCount: children.length,\n ...(options.tone ? { tone: options.tone } : {}),\n };\n\n const groupNode = {\n id: options.groupId,\n type: FLOW_GROUP_NODE_TYPE,\n position: { x: originX, y: originY },\n width: bounds.width + padding * 2,\n height: bounds.height + padding * 2 + GROUP_HEADER_OFFSET,\n data,\n } as unknown as NodeType;\n\n const reparented = new Map<string, NodeType>();\n for (const child of children) {\n reparented.set(child.id, {\n ...child,\n parentId: options.groupId,\n extent: \"parent\",\n position: { x: child.position.x - originX, y: child.position.y - originY },\n } as NodeType);\n }\n\n let earliest = nodes.length;\n for (let i = 0; i < nodes.length; i++) {\n if (idSet.has(nodes[i]!.id)) {\n earliest = i;\n break;\n }\n }\n\n const mapped = nodes.map((n) => reparented.get(n.id) ?? n);\n const resultNodes = [...mapped.slice(0, earliest), groupNode, ...mapped.slice(earliest)];\n return { nodes: resultNodes, edges };\n}\n\n/**\n * Dissolve a group: remove the group node and restore its **direct** children to\n * absolute positions (dropping `parentId`/`extent`). If the group is currently\n * collapsed it is expanded first, so ungrouping a collapsed group is safe.\n */\nexport function ungroup<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n groupId: string,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const group = nodes.find((n) => n.id === groupId);\n if (!group) return { nodes, edges };\n\n const base = (group.data as FlowGroupNodeData | undefined)?.collapsed\n ? expandGroup(nodes, edges, groupId)\n : { nodes, edges };\n\n const liveGroup = base.nodes.find((n) => n.id === groupId)!;\n const gx = liveGroup.position.x;\n const gy = liveGroup.position.y;\n\n const resultNodes = base.nodes\n .filter((n) => n.id !== groupId)\n .map((n) => {\n if (n.parentId === groupId) {\n const {\n parentId: _parentId,\n extent: _extent,\n ...rest\n } = n as NodeType & {\n parentId?: string;\n extent?: unknown;\n };\n return {\n ...rest,\n position: { x: n.position.x + gx, y: n.position.y + gy },\n } as NodeType;\n }\n return n;\n });\n\n return { nodes: resultNodes, edges: base.edges };\n}\n\n/**\n * Collapse a group to an overview chip.\n *\n * - Hides every descendant (including nested groups and their subtrees).\n * - Shrinks the group to a fixed overview size and marks `data.collapsed`.\n * - Re-routes every edge that **crosses the collapse boundary** (exactly one\n * endpoint inside the collapsed subtree) to a **proxy edge** targeting the\n * group node; the original edge is hidden and stashed for restoration.\n * - Edges fully inside or fully outside the subtree are left untouched.\n *\n * The full pre-collapse graph is stashed on the group's `data` so `expandGroup`\n * is an exact inverse. Nested collapse is handled by snapshotting each\n * descendant's exact prior state (a subgroup that was already collapsed stays\n * collapsed on expand — no double-unhide).\n */\nexport function collapseGroup<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n groupId: string,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const group = nodes.find((n) => n.id === groupId);\n if (!group) return { nodes, edges };\n if ((group.data as FlowGroupNodeData | undefined)?.collapsed) return { nodes, edges };\n\n const descendantIds = collectDescendantIds(nodes, groupId);\n\n const descendantSnapshots = nodes.filter((n) => descendantIds.has(n.id));\n const directChildCount = nodes.filter((n) => n.parentId === groupId).length;\n\n const isInside = (id: string) => descendantIds.has(id);\n const reroutedEdges: EdgeType[] = [];\n const proxyEdges: EdgeType[] = [];\n for (const edge of edges) {\n const sourceInside = isInside(edge.source);\n const targetInside = isInside(edge.target);\n if (sourceInside === targetInside) continue; // fully inside or fully outside\n\n reroutedEdges.push(edge);\n const proxyData: FlowGroupProxyEdgeData = {\n ...(edge.data ?? {}),\n __flowGroupProxy: true,\n groupId,\n };\n proxyEdges.push({\n ...edge,\n id: proxyEdgeId(groupId, edge.id),\n source: sourceInside ? groupId : edge.source,\n target: targetInside ? groupId : edge.target,\n sourceHandle: undefined,\n targetHandle: undefined,\n data: proxyData,\n } as EdgeType);\n }\n\n const collapsedState: FlowGroupCollapsedState = {\n groupSnapshot: group,\n descendantSnapshots,\n reroutedEdges,\n };\n\n const resultNodes = nodes.map((n) => {\n if (n.id === groupId) {\n return {\n ...n,\n width: OVERVIEW_WIDTH,\n height: OVERVIEW_HEIGHT,\n data: {\n ...(n.data as Record<string, unknown>),\n collapsed: true,\n childCount: directChildCount,\n [COLLAPSED_STATE_KEY]: collapsedState,\n },\n } as NodeType;\n }\n if (descendantIds.has(n.id)) {\n return { ...n, hidden: true } as NodeType;\n }\n return n;\n });\n\n const reroutedIds = new Set(reroutedEdges.map((e) => e.id));\n const resultEdges = edges.map((e) =>\n reroutedIds.has(e.id) ? ({ ...e, hidden: true } as EdgeType) : e,\n );\n resultEdges.push(...proxyEdges);\n\n return { nodes: resultNodes, edges: resultEdges };\n}\n\n/**\n * Expand a collapsed group — the exact inverse of `collapseGroup`. Restores the\n * group node, un-hides the descendant subtree (to each node's exact prior\n * state), removes this group's proxy edges and restores the original edges.\n * No-op if the group isn't collapsed.\n */\nexport function expandGroup<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n groupId: string,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const group = nodes.find((n) => n.id === groupId);\n const state = group ? readCollapsedState(group) : undefined;\n if (!group || !state) return { nodes, edges };\n\n const snapshotById = new Map(state.descendantSnapshots.map((n) => [n.id, n]));\n const reroutedById = new Map(state.reroutedEdges.map((e) => [e.id, e]));\n\n const resultNodes = nodes.map((n) => {\n if (n.id === groupId) return state.groupSnapshot as NodeType;\n const snap = snapshotById.get(n.id);\n return snap ? (snap as NodeType) : n;\n });\n\n const resultEdges = edges\n .filter(\n (e) =>\n !(\n isFlowGroupProxyEdge(e) &&\n (e.data as Partial<FlowGroupProxyEdgeData> | undefined)?.groupId === groupId\n ),\n )\n .map((e) => {\n const original = reroutedById.get(e.id);\n return original ? (original as EdgeType) : e;\n });\n\n return { nodes: resultNodes, edges: resultEdges };\n}\n\n/** Collapse if expanded, expand if collapsed. */\nexport function toggleGroupCollapsed<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n groupId: string,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const group = nodes.find((n) => n.id === groupId);\n if (!group) return { nodes, edges };\n return (group.data as FlowGroupNodeData | undefined)?.collapsed\n ? expandGroup(nodes, edges, groupId)\n : collapseGroup(nodes, edges, groupId);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/flow-mini-map/flow-mini-map.tsx
|
|
4
|
+
import { MiniMap } from "@xyflow/react";
|
|
5
|
+
import { cn } from "@elabs-ai/components-ui/lib/cn";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
function FlowMiniMap({
|
|
8
|
+
className,
|
|
9
|
+
nodeColor = "var(--flow-minimap-node)",
|
|
10
|
+
nodeStrokeColor = "var(--flow-minimap-node)",
|
|
11
|
+
maskColor = "var(--flow-minimap-mask)",
|
|
12
|
+
...props
|
|
13
|
+
}) {
|
|
14
|
+
return /* @__PURE__ */ jsx(
|
|
15
|
+
MiniMap,
|
|
16
|
+
{
|
|
17
|
+
className: cn("!rounded-lg !bg-surface-elevated !shadow-ring-sm", className),
|
|
18
|
+
nodeColor,
|
|
19
|
+
nodeStrokeColor,
|
|
20
|
+
maskColor,
|
|
21
|
+
...props
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
FlowMiniMap
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=chunk-WN46RIOC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/flow-mini-map/flow-mini-map.tsx"],"sourcesContent":["import { MiniMap, type MiniMapProps, type Node } from \"@xyflow/react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nexport type FlowMiniMapProps<NodeType extends Node = Node> = MiniMapProps<NodeType>;\n\n/**\n * Branded preset over React Flow's `<MiniMap>` — sources node/mask colors from\n * the `--flow-minimap-*` tokens so the overview stays theme-aware (the raw\n * `MiniMap` re-export defaults to library colors and renders theme-blind,\n * especially under a low-chroma theme). Every prop can still be overridden; render\n * inside a `<CanvasShell>` (it needs the React Flow context via `useStore`).\n */\nexport function FlowMiniMap<NodeType extends Node = Node>({\n className,\n nodeColor = \"var(--flow-minimap-node)\",\n nodeStrokeColor = \"var(--flow-minimap-node)\",\n maskColor = \"var(--flow-minimap-mask)\",\n ...props\n}: FlowMiniMapProps<NodeType>) {\n return (\n <MiniMap\n className={cn(\"!rounded-lg !bg-surface-elevated !shadow-ring-sm\", className)}\n nodeColor={nodeColor}\n nodeStrokeColor={nodeStrokeColor}\n maskColor={maskColor}\n {...props}\n />\n );\n}\n"],"mappings":";;;AAAA,SAAS,eAA6C;AACtD,SAAS,UAAU;AAmBf;AARG,SAAS,YAA0C;AAAA,EACxD;AAAA,EACA,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,GAAG;AACL,GAA+B;AAC7B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,oDAAoD,SAAS;AAAA,MAC3E;AAAA,MACA;AAAA,MACA;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
FlowEdgePath
|
|
4
|
+
} from "./chunk-SXXJV6YJ.js";
|
|
5
|
+
|
|
6
|
+
// src/flow-button-edge/flow-button-edge.tsx
|
|
7
|
+
import { EdgeLabelRenderer, getBezierPath } from "@xyflow/react";
|
|
8
|
+
import { Plus } from "lucide-react";
|
|
9
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
function FlowButtonEdge({
|
|
11
|
+
id,
|
|
12
|
+
sourceX,
|
|
13
|
+
sourceY,
|
|
14
|
+
targetX,
|
|
15
|
+
targetY,
|
|
16
|
+
sourcePosition,
|
|
17
|
+
targetPosition,
|
|
18
|
+
markerEnd,
|
|
19
|
+
style,
|
|
20
|
+
data
|
|
21
|
+
}) {
|
|
22
|
+
const [edgePath, labelX, labelY] = getBezierPath({
|
|
23
|
+
sourceX,
|
|
24
|
+
sourceY,
|
|
25
|
+
sourcePosition,
|
|
26
|
+
targetX,
|
|
27
|
+
targetY,
|
|
28
|
+
targetPosition
|
|
29
|
+
});
|
|
30
|
+
const label = data?.label ?? "Insert node on edge";
|
|
31
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
32
|
+
/* @__PURE__ */ jsx(
|
|
33
|
+
FlowEdgePath,
|
|
34
|
+
{
|
|
35
|
+
id,
|
|
36
|
+
path: edgePath,
|
|
37
|
+
markerEnd,
|
|
38
|
+
"data-slot": "flow-button-edge",
|
|
39
|
+
stroke: "var(--flow-edge)",
|
|
40
|
+
strokeWidth: 1.5,
|
|
41
|
+
style
|
|
42
|
+
}
|
|
43
|
+
),
|
|
44
|
+
/* @__PURE__ */ jsx(EdgeLabelRenderer, { children: /* @__PURE__ */ jsx(
|
|
45
|
+
"div",
|
|
46
|
+
{
|
|
47
|
+
style: {
|
|
48
|
+
position: "absolute",
|
|
49
|
+
transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`
|
|
50
|
+
},
|
|
51
|
+
className: "nodrag nopan pointer-events-auto",
|
|
52
|
+
children: /* @__PURE__ */ jsx(
|
|
53
|
+
"button",
|
|
54
|
+
{
|
|
55
|
+
type: "button",
|
|
56
|
+
"aria-label": label,
|
|
57
|
+
onClick: () => data?.onInsert?.(),
|
|
58
|
+
className: "flex size-5 items-center justify-center rounded-full border border-flow-group-border bg-flow-node text-flow-node-foreground shadow-sm transition-colors duration-fast ease-standard hover:bg-accent hover:text-accent-foreground focus-ring",
|
|
59
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "size-3", "aria-hidden": "true" })
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
) })
|
|
64
|
+
] });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export {
|
|
68
|
+
FlowButtonEdge
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=chunk-XZFE2YYH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/flow-button-edge/flow-button-edge.tsx"],"sourcesContent":["import { EdgeLabelRenderer, getBezierPath, type Edge, type EdgeProps } from \"@xyflow/react\";\nimport { Plus } from \"lucide-react\";\nimport { FlowEdgePath } from \"../flow-edge-path\";\n\nexport interface FlowButtonEdgeData extends Record<string, unknown> {\n /** aria-label for the insert button. @default \"Insert node on edge\" */\n label?: string;\n /** Called when the edge's \"+\" button is activated (click, Enter, or Space). */\n onInsert?: () => void;\n}\n\nexport type BrandFlowButtonEdge = Edge<FlowButtonEdgeData, \"button\">;\n\n/**\n * Branded bezier edge (matching `FlowEdge`'s `--flow-edge` token) with a\n * centered \"+\" button rendered via `EdgeLabelRenderer` at the edge midpoint.\n * Register it in `edgeTypes={{ button: FlowButtonEdge }}` and create edges\n * with `type: \"button\"` and `data: FlowButtonEdgeData`.\n *\n * The button is a real `<button>` with an `aria-label`, keyboard-activatable\n * via native Enter/Space. Fires `data.onInsert?.()` on activation — the\n * typical handler splits the edge: insert a new node between source and\n * target and rewire the two edges (see the \"Insert between\" story).\n */\nexport function FlowButtonEdge({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n markerEnd,\n style,\n data,\n}: EdgeProps<BrandFlowButtonEdge>) {\n const [edgePath, labelX, labelY] = getBezierPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n });\n const label = data?.label ?? \"Insert node on edge\";\n\n return (\n <>\n <FlowEdgePath\n id={id}\n path={edgePath}\n markerEnd={markerEnd}\n data-slot=\"flow-button-edge\"\n stroke=\"var(--flow-edge)\"\n strokeWidth={1.5}\n style={style}\n />\n <EdgeLabelRenderer>\n <div\n style={{\n position: \"absolute\",\n transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,\n }}\n className=\"nodrag nopan pointer-events-auto\"\n >\n <button\n type=\"button\"\n aria-label={label}\n onClick={() => data?.onInsert?.()}\n className=\"flex size-5 items-center justify-center rounded-full border border-flow-group-border bg-flow-node text-flow-node-foreground shadow-sm transition-colors duration-fast ease-standard hover:bg-accent hover:text-accent-foreground focus-ring\"\n >\n <Plus className=\"size-3\" aria-hidden=\"true\" />\n </button>\n </div>\n </EdgeLabelRenderer>\n </>\n );\n}\n"],"mappings":";;;;;;AAAA,SAAS,mBAAmB,qBAAgD;AAC5E,SAAS,YAAY;AA8CjB,mBACE,KADF;AAvBG,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAmC;AACjC,QAAM,CAAC,UAAU,QAAQ,MAAM,IAAI,cAAc;AAAA,IAC/C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,QAAM,QAAQ,MAAM,SAAS;AAE7B,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA,aAAU;AAAA,QACV,QAAO;AAAA,QACP,aAAa;AAAA,QACb;AAAA;AAAA,IACF;AAAA,IACA,oBAAC,qBACC;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,UACL,UAAU;AAAA,UACV,WAAW,mCAAmC,MAAM,OAAO,MAAM;AAAA,QACnE;AAAA,QACA,WAAU;AAAA,QAEV;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAY;AAAA,YACZ,SAAS,MAAM,MAAM,WAAW;AAAA,YAChC,WAAU;AAAA,YAEV,8BAAC,QAAK,WAAU,UAAS,eAAY,QAAO;AAAA;AAAA,QAC9C;AAAA;AAAA,IACF,GACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
getHelperLines
|
|
4
|
+
} from "./chunk-DNZRAJAQ.js";
|
|
5
|
+
|
|
6
|
+
// src/helper-lines/use-helper-lines.ts
|
|
7
|
+
import { useCallback, useState } from "react";
|
|
8
|
+
import { useReactFlow } from "@xyflow/react";
|
|
9
|
+
function nodeRect(node) {
|
|
10
|
+
return {
|
|
11
|
+
x: node.position.x,
|
|
12
|
+
y: node.position.y,
|
|
13
|
+
width: node.measured?.width ?? node.width ?? 0,
|
|
14
|
+
height: node.measured?.height ?? node.height ?? 0
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function useHelperLines(onNodesChange, options = {}) {
|
|
18
|
+
const { threshold = 5 } = options;
|
|
19
|
+
const { getNodes } = useReactFlow();
|
|
20
|
+
const [helperLineHorizontal, setHelperLineHorizontal] = useState();
|
|
21
|
+
const [helperLineVertical, setHelperLineVertical] = useState();
|
|
22
|
+
const handleNodesChange = useCallback(
|
|
23
|
+
(changes) => {
|
|
24
|
+
let horizontal;
|
|
25
|
+
let vertical;
|
|
26
|
+
const dragChanges = changes.filter(
|
|
27
|
+
(c) => c.type === "position" && c.dragging === true && c.position != null
|
|
28
|
+
);
|
|
29
|
+
if (dragChanges.length === 1) {
|
|
30
|
+
const change = dragChanges[0];
|
|
31
|
+
const nodes = getNodes();
|
|
32
|
+
const dragged = change ? nodes.find((n) => n.id === change.id) : void 0;
|
|
33
|
+
if (change && change.position && dragged) {
|
|
34
|
+
const draggedRect = {
|
|
35
|
+
x: change.position.x,
|
|
36
|
+
y: change.position.y,
|
|
37
|
+
width: dragged.measured?.width ?? dragged.width ?? 0,
|
|
38
|
+
height: dragged.measured?.height ?? dragged.height ?? 0
|
|
39
|
+
};
|
|
40
|
+
const others = nodes.filter((n) => n.id !== change.id).map(nodeRect);
|
|
41
|
+
const {
|
|
42
|
+
snapX,
|
|
43
|
+
snapY,
|
|
44
|
+
vertical: v,
|
|
45
|
+
horizontal: h
|
|
46
|
+
} = getHelperLines(draggedRect, others, threshold);
|
|
47
|
+
if (snapX !== void 0) change.position.x = snapX;
|
|
48
|
+
if (snapY !== void 0) change.position.y = snapY;
|
|
49
|
+
vertical = v;
|
|
50
|
+
horizontal = h;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
setHelperLineHorizontal(horizontal);
|
|
54
|
+
setHelperLineVertical(vertical);
|
|
55
|
+
onNodesChange?.(changes);
|
|
56
|
+
},
|
|
57
|
+
[getNodes, onNodesChange, threshold]
|
|
58
|
+
);
|
|
59
|
+
return { onNodesChange: handleNodesChange, helperLineHorizontal, helperLineVertical };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
useHelperLines
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=chunk-ZATVF42E.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/helper-lines/use-helper-lines.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useState } from \"react\";\nimport { useReactFlow, type Node, type NodeChange } from \"@xyflow/react\";\nimport { getHelperLines, type HelperLineRect } from \"./get-helper-lines\";\n\nexport interface UseHelperLinesOptions {\n /** Alignment threshold in flow coordinates. Defaults to 5. */\n threshold?: number;\n}\n\nexport interface UseHelperLinesResult<NodeType extends Node = Node> {\n /**\n * Drop-in replacement for the consumer's `onNodesChange`: it snaps a dragged\n * node onto any near-aligned edge/center, then forwards the (mutated) changes\n * to the wrapped handler.\n */\n onNodesChange: (changes: NodeChange<NodeType>[]) => void;\n /** Absolute flow-y of the active horizontal guide, or `undefined`. */\n helperLineHorizontal: number | undefined;\n /** Absolute flow-x of the active vertical guide, or `undefined`. */\n helperLineVertical: number | undefined;\n}\n\nfunction nodeRect(node: Node): HelperLineRect {\n return {\n x: node.position.x,\n y: node.position.y,\n width: node.measured?.width ?? node.width ?? 0,\n height: node.measured?.height ?? node.height ?? 0,\n };\n}\n\n/**\n * Decorate a consumer's `onNodesChange` with alignment guides + snapping.\n *\n * While a **single** node is dragged, the returned handler compares its\n * left/center/right and top/middle/bottom against every other node; when a pair\n * is within `threshold` flow px it (a) snaps the dragged node's position change\n * onto the guide and (b) exposes the guide coordinates so `<HelperLines>` can\n * draw them. Guides clear when the drag ends or nothing aligns.\n *\n * Must be called inside a React Flow context (a `<ReactFlowProvider>` or a\n * component rendered under `<ReactFlow>`), since it reads the live node store.\n *\n * @example\n * const [nodes, , onNodesChange] = useNodesState(initialNodes);\n * const { onNodesChange: handle, helperLineHorizontal, helperLineVertical } =\n * useHelperLines(onNodesChange);\n * // <ReactFlow onNodesChange={handle}>\n * // <HelperLines horizontal={helperLineHorizontal} vertical={helperLineVertical} />\n */\nexport function useHelperLines<NodeType extends Node = Node>(\n onNodesChange?: (changes: NodeChange<NodeType>[]) => void,\n options: UseHelperLinesOptions = {},\n): UseHelperLinesResult<NodeType> {\n const { threshold = 5 } = options;\n const { getNodes } = useReactFlow<NodeType>();\n const [helperLineHorizontal, setHelperLineHorizontal] = useState<number>();\n const [helperLineVertical, setHelperLineVertical] = useState<number>();\n\n const handleNodesChange = useCallback(\n (changes: NodeChange<NodeType>[]) => {\n let horizontal: number | undefined;\n let vertical: number | undefined;\n\n const dragChanges = changes.filter(\n (c): c is Extract<NodeChange<NodeType>, { type: \"position\" }> =>\n c.type === \"position\" && c.dragging === true && c.position != null,\n );\n\n // Only guide/snap a single dragged node — multi-select drags are ambiguous.\n if (dragChanges.length === 1) {\n const change = dragChanges[0];\n const nodes = getNodes();\n const dragged = change ? nodes.find((n) => n.id === change.id) : undefined;\n\n if (change && change.position && dragged) {\n const draggedRect: HelperLineRect = {\n x: change.position.x,\n y: change.position.y,\n width: dragged.measured?.width ?? dragged.width ?? 0,\n height: dragged.measured?.height ?? dragged.height ?? 0,\n };\n const others = nodes.filter((n) => n.id !== change.id).map(nodeRect);\n const {\n snapX,\n snapY,\n vertical: v,\n horizontal: h,\n } = getHelperLines(draggedRect, others, threshold);\n\n // Mutate the position change so the node lands exactly on the guide.\n if (snapX !== undefined) change.position.x = snapX;\n if (snapY !== undefined) change.position.y = snapY;\n vertical = v;\n horizontal = h;\n }\n }\n\n setHelperLineHorizontal(horizontal);\n setHelperLineVertical(vertical);\n onNodesChange?.(changes);\n },\n [getNodes, onNodesChange, threshold],\n );\n\n return { onNodesChange: handleNodesChange, helperLineHorizontal, helperLineVertical };\n}\n"],"mappings":";;;;;;AAEA,SAAS,aAAa,gBAAgB;AACtC,SAAS,oBAAgD;AAqBzD,SAAS,SAAS,MAA4B;AAC5C,SAAO;AAAA,IACL,GAAG,KAAK,SAAS;AAAA,IACjB,GAAG,KAAK,SAAS;AAAA,IACjB,OAAO,KAAK,UAAU,SAAS,KAAK,SAAS;AAAA,IAC7C,QAAQ,KAAK,UAAU,UAAU,KAAK,UAAU;AAAA,EAClD;AACF;AAqBO,SAAS,eACd,eACA,UAAiC,CAAC,GACF;AAChC,QAAM,EAAE,YAAY,EAAE,IAAI;AAC1B,QAAM,EAAE,SAAS,IAAI,aAAuB;AAC5C,QAAM,CAAC,sBAAsB,uBAAuB,IAAI,SAAiB;AACzE,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,SAAiB;AAErE,QAAM,oBAAoB;AAAA,IACxB,CAAC,YAAoC;AACnC,UAAI;AACJ,UAAI;AAEJ,YAAM,cAAc,QAAQ;AAAA,QAC1B,CAAC,MACC,EAAE,SAAS,cAAc,EAAE,aAAa,QAAQ,EAAE,YAAY;AAAA,MAClE;AAGA,UAAI,YAAY,WAAW,GAAG;AAC5B,cAAM,SAAS,YAAY,CAAC;AAC5B,cAAM,QAAQ,SAAS;AACvB,cAAM,UAAU,SAAS,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI;AAEjE,YAAI,UAAU,OAAO,YAAY,SAAS;AACxC,gBAAM,cAA8B;AAAA,YAClC,GAAG,OAAO,SAAS;AAAA,YACnB,GAAG,OAAO,SAAS;AAAA,YACnB,OAAO,QAAQ,UAAU,SAAS,QAAQ,SAAS;AAAA,YACnD,QAAQ,QAAQ,UAAU,UAAU,QAAQ,UAAU;AAAA,UACxD;AACA,gBAAM,SAAS,MAAM,OAAO,CAAC,MAAM,EAAE,OAAO,OAAO,EAAE,EAAE,IAAI,QAAQ;AACnE,gBAAM;AAAA,YACJ;AAAA,YACA;AAAA,YACA,UAAU;AAAA,YACV,YAAY;AAAA,UACd,IAAI,eAAe,aAAa,QAAQ,SAAS;AAGjD,cAAI,UAAU,OAAW,QAAO,SAAS,IAAI;AAC7C,cAAI,UAAU,OAAW,QAAO,SAAS,IAAI;AAC7C,qBAAW;AACX,uBAAa;AAAA,QACf;AAAA,MACF;AAEA,8BAAwB,UAAU;AAClC,4BAAsB,QAAQ;AAC9B,sBAAgB,OAAO;AAAA,IACzB;AAAA,IACA,CAAC,UAAU,eAAe,SAAS;AAAA,EACrC;AAEA,SAAO,EAAE,eAAe,mBAAmB,sBAAsB,mBAAmB;AACtF;","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/flow-weighted-edge/back-edge-geometry.ts
|
|
4
|
+
function backEdgeDetour(rects, axis, span, clearance) {
|
|
5
|
+
const [lo, hi] = span[0] <= span[1] ? span : [span[1], span[0]];
|
|
6
|
+
let far = Number.NEGATIVE_INFINITY;
|
|
7
|
+
for (const rect of rects) {
|
|
8
|
+
if (!(rect.width > 0) || !(rect.height > 0)) continue;
|
|
9
|
+
const [near, beyond] = axis === "vertical" ? [rect.y, rect.y + rect.height] : [rect.x, rect.x + rect.width];
|
|
10
|
+
if (beyond < lo || near > hi) continue;
|
|
11
|
+
far = Math.max(far, axis === "vertical" ? rect.x + rect.width : rect.y + rect.height);
|
|
12
|
+
}
|
|
13
|
+
return Number.isFinite(far) ? far + clearance : null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
backEdgeDetour
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=chunk-ZXZTLQIX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/flow-weighted-edge/back-edge-geometry.ts"],"sourcesContent":["/**\n * Where a back edge's return leg runs, so it stays VISIBLE.\n *\n * A back edge is drawn as a smoothstep with one long cross-segment running against the\n * layout direction. React Flow puts that segment halfway between the two handles by\n * default, and a fixed nudge off the midpoint does not help: in a top-down layout the\n * midpoint of a rework edge sits between two ranks, i.e. squarely inside the column of\n * cards it is supposed to run past. Edges paint UNDER nodes, so the segment vanishes\n * behind the cards and all the reader is left with is a dashed stub below one node and\n * another above the other — which reads as a stray rectangle, not as a loop back.\n *\n * So the leg is placed OUTSIDE every card it would otherwise pass behind: past the far\n * side of every node whose extent overlaps the span the edge crosses. That is how a\n * process-mining tool draws a rework loop — out, around the rank, back in — and it is\n * the only placement that is correct for a graph rather than for a pair of nodes.\n */\n\n/** One node's laid-out box, in flow coordinates. */\nexport interface BackEdgeNodeRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/** Which axis the layout's ranks advance along. */\nexport type BackEdgeAxis = \"vertical\" | \"horizontal\";\n\n/**\n * The `centerX` (vertical layout) or `centerY` (horizontal layout) to hand\n * `getSmoothStepPath`, or `null` when nothing is measured yet and React Flow's own\n * midpoint should stand.\n *\n * @param rects Every laid-out node on the canvas. Nodes that cannot be hit are filtered\n * out here rather than by the caller, so the caller stays a plain map.\n * @param span The interval the edge crosses on the RANK axis (`[min, max]` of the two\n * handle coordinates) — a node outside it is never behind this edge.\n * @param clearance Gap left between the leg and the outermost card it clears.\n */\nexport function backEdgeDetour(\n rects: readonly BackEdgeNodeRect[],\n axis: BackEdgeAxis,\n span: readonly [number, number],\n clearance: number,\n): number | null {\n const [lo, hi] = span[0] <= span[1] ? span : [span[1], span[0]];\n let far = Number.NEGATIVE_INFINITY;\n\n for (const rect of rects) {\n if (!(rect.width > 0) || !(rect.height > 0)) continue;\n // Overlap on the RANK axis (the one the edge travels along): a node beside the\n // corridor but on another rank cannot be crossed by this leg.\n const [near, beyond] =\n axis === \"vertical\" ? [rect.y, rect.y + rect.height] : [rect.x, rect.x + rect.width];\n if (beyond < lo || near > hi) continue;\n far = Math.max(far, axis === \"vertical\" ? rect.x + rect.width : rect.y + rect.height);\n }\n\n return Number.isFinite(far) ? far + clearance : null;\n}\n"],"mappings":";;;AAuCO,SAAS,eACd,OACA,MACA,MACA,WACe;AACf,QAAM,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC;AAC9D,MAAI,MAAM,OAAO;AAEjB,aAAW,QAAQ,OAAO;AACxB,QAAI,EAAE,KAAK,QAAQ,MAAM,EAAE,KAAK,SAAS,GAAI;AAG7C,UAAM,CAAC,MAAM,MAAM,IACjB,SAAS,aAAa,CAAC,KAAK,GAAG,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC,KAAK,GAAG,KAAK,IAAI,KAAK,KAAK;AACrF,QAAI,SAAS,MAAM,OAAO,GAAI;AAC9B,UAAM,KAAK,IAAI,KAAK,SAAS,aAAa,KAAK,IAAI,KAAK,QAAQ,KAAK,IAAI,KAAK,MAAM;AAAA,EACtF;AAEA,SAAO,OAAO,SAAS,GAAG,IAAI,MAAM,YAAY;AAClD;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|