@particle-academy/fancy-flow 0.19.0 → 0.20.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/dist/{chunk-FSC64BPD.js → chunk-AW2QUEL2.js} +3 -3
- package/dist/{chunk-FSC64BPD.js.map → chunk-AW2QUEL2.js.map} +1 -1
- package/dist/{chunk-PHX4SBOD.js → chunk-KI4RI2N3.js} +3 -3
- package/dist/{chunk-PHX4SBOD.js.map → chunk-KI4RI2N3.js.map} +1 -1
- package/dist/{chunk-NF6NPY5N.js → chunk-XETAGLH6.js} +31 -3
- package/dist/chunk-XETAGLH6.js.map +1 -0
- package/dist/index.cjs +188 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -2
- package/dist/index.d.ts +48 -2
- package/dist/index.js +163 -8
- package/dist/index.js.map +1 -1
- package/dist/registry.js +2 -2
- package/dist/runtime.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-NF6NPY5N.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -4871,6 +4871,28 @@ var addEdge = (edgeParams, edges, options = {}) => {
|
|
|
4871
4871
|
}
|
|
4872
4872
|
return edges.concat(edge);
|
|
4873
4873
|
};
|
|
4874
|
+
var reconnectEdge = (oldEdge, newConnection, edges, options = { shouldReplaceId: true }) => {
|
|
4875
|
+
const { id: oldEdgeId, ...rest } = oldEdge;
|
|
4876
|
+
if (!newConnection.source || !newConnection.target) {
|
|
4877
|
+
options.onError?.("006", errorMessages["error006"]());
|
|
4878
|
+
return edges;
|
|
4879
|
+
}
|
|
4880
|
+
const foundEdge = edges.find((e) => e.id === oldEdge.id);
|
|
4881
|
+
if (!foundEdge) {
|
|
4882
|
+
options.onError?.("007", errorMessages["error007"](oldEdgeId));
|
|
4883
|
+
return edges;
|
|
4884
|
+
}
|
|
4885
|
+
const edgeIdGenerator = options.getEdgeId || getEdgeId;
|
|
4886
|
+
const edge = {
|
|
4887
|
+
...rest,
|
|
4888
|
+
id: options.shouldReplaceId ? edgeIdGenerator(newConnection) : oldEdgeId,
|
|
4889
|
+
source: newConnection.source,
|
|
4890
|
+
target: newConnection.target,
|
|
4891
|
+
sourceHandle: newConnection.sourceHandle,
|
|
4892
|
+
targetHandle: newConnection.targetHandle
|
|
4893
|
+
};
|
|
4894
|
+
return edges.filter((e) => e.id !== oldEdgeId).concat(edge);
|
|
4895
|
+
};
|
|
4874
4896
|
function getStraightPath({ sourceX, sourceY, targetX, targetY }) {
|
|
4875
4897
|
const [labelX, labelY, offsetX, offsetY] = getEdgeCenter({
|
|
4876
4898
|
sourceX,
|
|
@@ -7509,6 +7531,12 @@ function addEdge2(edgeParams, edges, options = {}) {
|
|
|
7509
7531
|
onError: options.onError ?? defaultOnError
|
|
7510
7532
|
});
|
|
7511
7533
|
}
|
|
7534
|
+
function reconnectEdge2(oldEdge, newConnection, edges, options = { shouldReplaceId: true }) {
|
|
7535
|
+
return reconnectEdge(oldEdge, newConnection, edges, {
|
|
7536
|
+
...options,
|
|
7537
|
+
onError: options.onError ?? defaultOnError
|
|
7538
|
+
});
|
|
7539
|
+
}
|
|
7512
7540
|
var isNode = (element) => isNodeBase(element);
|
|
7513
7541
|
var isEdge = (element) => isEdgeBase(element);
|
|
7514
7542
|
function fixedForwardRef(render) {
|
|
@@ -11523,6 +11551,65 @@ function duplicateNode(node, id2, offset = 40) {
|
|
|
11523
11551
|
data: JSON.parse(JSON.stringify(node.data ?? {}))
|
|
11524
11552
|
};
|
|
11525
11553
|
}
|
|
11554
|
+
function cloneSubgraph(nodes, edges, opts) {
|
|
11555
|
+
const offset = opts.offset ?? 40;
|
|
11556
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
11557
|
+
for (const n of nodes) idMap.set(n.id, opts.makeId());
|
|
11558
|
+
const clonedNodes = nodes.map((n) => {
|
|
11559
|
+
const cloned = duplicateNode(n, idMap.get(n.id), offset);
|
|
11560
|
+
const parentId = n.parentId;
|
|
11561
|
+
if (parentId && idMap.has(parentId)) cloned.parentId = idMap.get(parentId);
|
|
11562
|
+
else if (parentId) delete cloned.parentId;
|
|
11563
|
+
return cloned;
|
|
11564
|
+
});
|
|
11565
|
+
const clonedEdges = edges.filter((e) => idMap.has(e.source) && idMap.has(e.target)).map((e) => ({ ...e, id: opts.makeId(), source: idMap.get(e.source), target: idMap.get(e.target) }));
|
|
11566
|
+
return { nodes: clonedNodes, edges: clonedEdges, idMap };
|
|
11567
|
+
}
|
|
11568
|
+
function reconnectEdge3(edges, oldEdge, newConnection) {
|
|
11569
|
+
return reconnectEdge2(oldEdge, newConnection, edges, { shouldReplaceId: false });
|
|
11570
|
+
}
|
|
11571
|
+
var nodeW = (n) => n.width ?? n.measured?.width ?? 0;
|
|
11572
|
+
var nodeH = (n) => n.height ?? n.measured?.height ?? 0;
|
|
11573
|
+
function alignNodes(nodes, edge) {
|
|
11574
|
+
if (nodes.length < 2) return nodes;
|
|
11575
|
+
const minL = Math.min(...nodes.map((n) => n.position.x));
|
|
11576
|
+
const maxR = Math.max(...nodes.map((n) => n.position.x + nodeW(n)));
|
|
11577
|
+
const minT = Math.min(...nodes.map((n) => n.position.y));
|
|
11578
|
+
const maxB = Math.max(...nodes.map((n) => n.position.y + nodeH(n)));
|
|
11579
|
+
const cx = (minL + maxR) / 2;
|
|
11580
|
+
const cy = (minT + maxB) / 2;
|
|
11581
|
+
return nodes.map((n) => {
|
|
11582
|
+
let { x, y } = n.position;
|
|
11583
|
+
if (edge === "left") x = minL;
|
|
11584
|
+
else if (edge === "right") x = maxR - nodeW(n);
|
|
11585
|
+
else if (edge === "hcenter") x = cx - nodeW(n) / 2;
|
|
11586
|
+
else if (edge === "top") y = minT;
|
|
11587
|
+
else if (edge === "bottom") y = maxB - nodeH(n);
|
|
11588
|
+
else if (edge === "vcenter") y = cy - nodeH(n) / 2;
|
|
11589
|
+
return { ...n, position: { x, y } };
|
|
11590
|
+
});
|
|
11591
|
+
}
|
|
11592
|
+
function distributeNodes(nodes, axis) {
|
|
11593
|
+
if (nodes.length < 3) return nodes;
|
|
11594
|
+
const size = (n) => axis === "h" ? nodeW(n) : nodeH(n);
|
|
11595
|
+
const coord = (n) => axis === "h" ? n.position.x : n.position.y;
|
|
11596
|
+
const sorted = [...nodes].sort((a, b) => coord(a) - coord(b));
|
|
11597
|
+
const start2 = coord(sorted[0]);
|
|
11598
|
+
const last = sorted[sorted.length - 1];
|
|
11599
|
+
const end = coord(last) + size(last);
|
|
11600
|
+
const totalSize = sorted.reduce((s, n) => s + size(n), 0);
|
|
11601
|
+
const gap = (end - start2 - totalSize) / (sorted.length - 1);
|
|
11602
|
+
const posById = /* @__PURE__ */ new Map();
|
|
11603
|
+
let cursor = start2;
|
|
11604
|
+
for (const n of sorted) {
|
|
11605
|
+
posById.set(n.id, cursor);
|
|
11606
|
+
cursor += size(n) + gap;
|
|
11607
|
+
}
|
|
11608
|
+
return nodes.map((n) => {
|
|
11609
|
+
const p = posById.get(n.id);
|
|
11610
|
+
return { ...n, position: axis === "h" ? { ...n.position, x: p } : { ...n.position, y: p } };
|
|
11611
|
+
});
|
|
11612
|
+
}
|
|
11526
11613
|
var FlowEditorContext = ReactExports.createContext(null);
|
|
11527
11614
|
var FlowEditorProvider = FlowEditorContext.Provider;
|
|
11528
11615
|
function useFlowEditor() {
|
|
@@ -11692,6 +11779,89 @@ function FlowEditorInner({
|
|
|
11692
11779
|
},
|
|
11693
11780
|
[flow, onEdgeDelete, confirmDelete]
|
|
11694
11781
|
);
|
|
11782
|
+
const selectedNodes = ReactExports.useMemo(() => flow.nodes.filter((n) => n.selected), [flow.nodes]);
|
|
11783
|
+
const selectedIds = ReactExports.useMemo(() => selectedNodes.map((n) => n.id), [selectedNodes]);
|
|
11784
|
+
const clipboard = ReactExports.useRef(null);
|
|
11785
|
+
const copySelection = ReactExports.useCallback(() => {
|
|
11786
|
+
const sel = flow.nodes.filter((n) => n.selected);
|
|
11787
|
+
if (sel.length === 0) return;
|
|
11788
|
+
const ids = new Set(sel.map((n) => n.id));
|
|
11789
|
+
const edges = flow.edges.filter((e) => ids.has(e.source) && ids.has(e.target));
|
|
11790
|
+
clipboard.current = { nodes: sel.map((n) => ({ ...n })), edges: edges.map((e) => ({ ...e })) };
|
|
11791
|
+
}, [flow]);
|
|
11792
|
+
const pasteClipboard = ReactExports.useCallback(
|
|
11793
|
+
(at) => {
|
|
11794
|
+
const clip = clipboard.current;
|
|
11795
|
+
if (!clip || clip.nodes.length === 0) return;
|
|
11796
|
+
const { nodes: clones, edges: cloneEdges } = cloneSubgraph(clip.nodes, clip.edges, { makeId: newNodeId, offset: 40 });
|
|
11797
|
+
let placed = clones;
|
|
11798
|
+
if (at) {
|
|
11799
|
+
const minX = Math.min(...clones.map((n) => n.position.x));
|
|
11800
|
+
const minY = Math.min(...clones.map((n) => n.position.y));
|
|
11801
|
+
placed = clones.map((n) => ({ ...n, position: { x: n.position.x - minX + at.x, y: n.position.y - minY + at.y } }));
|
|
11802
|
+
}
|
|
11803
|
+
flow.setGraph({
|
|
11804
|
+
nodes: [...flow.nodes.map((n) => ({ ...n, selected: false })), ...placed.map((n) => ({ ...n, selected: true }))],
|
|
11805
|
+
edges: [...flow.edges, ...cloneEdges]
|
|
11806
|
+
});
|
|
11807
|
+
},
|
|
11808
|
+
[flow]
|
|
11809
|
+
);
|
|
11810
|
+
const duplicateSelected = ReactExports.useCallback(() => {
|
|
11811
|
+
const sel = flow.nodes.filter((n) => n.selected);
|
|
11812
|
+
if (sel.length === 0) return;
|
|
11813
|
+
const ids = new Set(sel.map((n) => n.id));
|
|
11814
|
+
const internal2 = flow.edges.filter((e) => ids.has(e.source) && ids.has(e.target));
|
|
11815
|
+
const { nodes: clones, edges: cloneEdges } = cloneSubgraph(sel, internal2, { makeId: newNodeId, offset: 40 });
|
|
11816
|
+
flow.setGraph({
|
|
11817
|
+
nodes: [...flow.nodes.map((n) => ({ ...n, selected: false })), ...clones.map((n) => ({ ...n, selected: true }))],
|
|
11818
|
+
edges: [...flow.edges, ...cloneEdges]
|
|
11819
|
+
});
|
|
11820
|
+
}, [flow]);
|
|
11821
|
+
const alignSelected = ReactExports.useCallback(
|
|
11822
|
+
(edge) => {
|
|
11823
|
+
const sel = flow.nodes.filter((n) => n.selected);
|
|
11824
|
+
if (sel.length < 2) return;
|
|
11825
|
+
const byId = new Map(alignNodes(sel, edge).map((n) => [n.id, n]));
|
|
11826
|
+
flow.setNodes((all) => all.map((n) => byId.get(n.id) ?? n));
|
|
11827
|
+
},
|
|
11828
|
+
[flow]
|
|
11829
|
+
);
|
|
11830
|
+
const distributeSelected = ReactExports.useCallback(
|
|
11831
|
+
(axis) => {
|
|
11832
|
+
const sel = flow.nodes.filter((n) => n.selected);
|
|
11833
|
+
if (sel.length < 3) return;
|
|
11834
|
+
const byId = new Map(distributeNodes(sel, axis).map((n) => [n.id, n]));
|
|
11835
|
+
flow.setNodes((all) => all.map((n) => byId.get(n.id) ?? n));
|
|
11836
|
+
},
|
|
11837
|
+
[flow]
|
|
11838
|
+
);
|
|
11839
|
+
const onReconnect = ReactExports.useCallback(
|
|
11840
|
+
(oldEdge, conn) => flow.setEdges((eds) => reconnectEdge3(eds, oldEdge, conn)),
|
|
11841
|
+
[flow]
|
|
11842
|
+
);
|
|
11843
|
+
ReactExports.useEffect(() => {
|
|
11844
|
+
const onKey = (e) => {
|
|
11845
|
+
const t = e.target;
|
|
11846
|
+
if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) return;
|
|
11847
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
11848
|
+
const key = e.key.toLowerCase();
|
|
11849
|
+
if (key === "c") copySelection();
|
|
11850
|
+
else if (key === "x") {
|
|
11851
|
+
e.preventDefault();
|
|
11852
|
+
copySelection();
|
|
11853
|
+
deleteNodes(selectedIds);
|
|
11854
|
+
} else if (key === "v") {
|
|
11855
|
+
e.preventDefault();
|
|
11856
|
+
pasteClipboard();
|
|
11857
|
+
} else if (key === "d") {
|
|
11858
|
+
e.preventDefault();
|
|
11859
|
+
duplicateSelected();
|
|
11860
|
+
}
|
|
11861
|
+
};
|
|
11862
|
+
window.addEventListener("keydown", onKey);
|
|
11863
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
11864
|
+
}, [copySelection, pasteClipboard, duplicateSelected, deleteNodes, selectedIds]);
|
|
11695
11865
|
const api = ReactExports.useMemo(() => {
|
|
11696
11866
|
const toWorkflow = () => exportWorkflow({ nodes: flow.nodes, edges: flow.edges }, metadata);
|
|
11697
11867
|
return {
|
|
@@ -11702,6 +11872,8 @@ function FlowEditorInner({
|
|
|
11702
11872
|
selected: selected2,
|
|
11703
11873
|
selectedEdgeId,
|
|
11704
11874
|
selectedEdge,
|
|
11875
|
+
selectedIds,
|
|
11876
|
+
selectedNodes,
|
|
11705
11877
|
running: runner.running,
|
|
11706
11878
|
statuses: runner.statuses,
|
|
11707
11879
|
select: setSelectedId,
|
|
@@ -11722,6 +11894,15 @@ function FlowEditorInner({
|
|
|
11722
11894
|
return copy.id;
|
|
11723
11895
|
},
|
|
11724
11896
|
setGraph: (graph) => flow.setGraph(graph),
|
|
11897
|
+
duplicateSelected,
|
|
11898
|
+
alignSelected,
|
|
11899
|
+
distributeSelected,
|
|
11900
|
+
copy: copySelection,
|
|
11901
|
+
cut: () => {
|
|
11902
|
+
copySelection();
|
|
11903
|
+
deleteNodes(selectedIds);
|
|
11904
|
+
},
|
|
11905
|
+
paste: pasteClipboard,
|
|
11725
11906
|
run: () => runner.run({ nodes: flow.nodes, edges: flow.edges }, executors),
|
|
11726
11907
|
cancel: runner.cancel,
|
|
11727
11908
|
reset: runner.reset,
|
|
@@ -11734,7 +11915,7 @@ function FlowEditorInner({
|
|
|
11734
11915
|
canUndo: hist.canUndo,
|
|
11735
11916
|
canRedo: hist.canRedo
|
|
11736
11917
|
};
|
|
11737
|
-
}, [flow, selectedId, selected2, selectedEdgeId, selectedEdge, runner, executors, metadata, addNode, deleteNodes, deleteEdges, hist, rf]);
|
|
11918
|
+
}, [flow, selectedId, selected2, selectedEdgeId, selectedEdge, selectedIds, selectedNodes, runner, executors, metadata, addNode, deleteNodes, deleteEdges, duplicateSelected, alignSelected, distributeSelected, copySelection, pasteClipboard, hist, rf]);
|
|
11738
11919
|
ReactExports.useImperativeHandle(apiRef, () => api, [api]);
|
|
11739
11920
|
const dropHandlers = paletteDropHandlers((kindName, evt) => {
|
|
11740
11921
|
const point = rf.screenToFlowPosition({ x: evt.clientX, y: evt.clientY });
|
|
@@ -11794,6 +11975,8 @@ function FlowEditorInner({
|
|
|
11794
11975
|
onNodesChange: flow.onNodesChange,
|
|
11795
11976
|
onEdgesChange: flow.onEdgesChange,
|
|
11796
11977
|
onConnect: flow.onConnect,
|
|
11978
|
+
onReconnect,
|
|
11979
|
+
edgesReconnectable: true,
|
|
11797
11980
|
onNodeDragStart: hist.onNodeDragStart,
|
|
11798
11981
|
onNodeClick: handleNodeClick2,
|
|
11799
11982
|
onNodeContextMenu: builtins.contextMenu === false ? void 0 : handleNodeContextMenu,
|
|
@@ -12083,9 +12266,11 @@ exports.SubgraphNode = SubgraphNode;
|
|
|
12083
12266
|
exports.TriggerNode = TriggerNode;
|
|
12084
12267
|
exports.WORKFLOW_SCHEMA_URL = WORKFLOW_SCHEMA_URL;
|
|
12085
12268
|
exports.WORKFLOW_SCHEMA_VERSION = WORKFLOW_SCHEMA_VERSION;
|
|
12269
|
+
exports.alignNodes = alignNodes;
|
|
12086
12270
|
exports.applyStatusesToNodes = applyStatusesToNodes;
|
|
12087
12271
|
exports.buildNodeTypes = buildNodeTypes;
|
|
12088
12272
|
exports.categoryAccent = categoryAccent;
|
|
12273
|
+
exports.cloneSubgraph = cloneSubgraph;
|
|
12089
12274
|
exports.createConnectionValidator = createConnectionValidator;
|
|
12090
12275
|
exports.createHistory = createHistory;
|
|
12091
12276
|
exports.decodePause = decodePause;
|
|
@@ -12093,6 +12278,7 @@ exports.defaultConfigFor = defaultConfigFor;
|
|
|
12093
12278
|
exports.defaultNodeTypes = defaultNodeTypes;
|
|
12094
12279
|
exports.defaultPortCompatibility = defaultPortCompatibility;
|
|
12095
12280
|
exports.defineNode = defineNode;
|
|
12281
|
+
exports.distributeNodes = distributeNodes;
|
|
12096
12282
|
exports.encodePause = encodePause;
|
|
12097
12283
|
exports.exportWorkflow = exportWorkflow;
|
|
12098
12284
|
exports.getNodeKind = getNodeKind;
|
|
@@ -12105,6 +12291,7 @@ exports.onNodeKindsChanged = onNodeKindsChanged;
|
|
|
12105
12291
|
exports.onRichInputAdapterChanged = onRichInputAdapterChanged;
|
|
12106
12292
|
exports.paletteDropHandlers = paletteDropHandlers;
|
|
12107
12293
|
exports.pauseForHuman = pauseForHuman;
|
|
12294
|
+
exports.reconnectEdge = reconnectEdge3;
|
|
12108
12295
|
exports.registerBuiltinKinds = registerBuiltinKinds;
|
|
12109
12296
|
exports.registerNodeKind = registerNodeKind;
|
|
12110
12297
|
exports.registerRichInputAdapter = registerRichInputAdapter;
|