@jalez/react-flow-automated-layout
Version:
A React library for automated layout of nested node graphs with parent-child relationships using React Flow
1,513 lines (1,512 loc) • 42.2 kB
JavaScript
import { jsx as d, jsxs as N, Fragment as V } from "react/jsx-runtime";
import { createContext as It, useContext as Gt, useState as k, useEffect as I, useRef as ht, useCallback as G } from "react";
import { Position as R, useReactFlow as _t, useNodes as $t, useEdges as Ft, useOnSelectionChange as Pt, ControlButton as M, Controls as Ut } from "@xyflow/react";
import dt from "@dagrejs/dagre";
const bt = It(void 0);
function _() {
const t = Gt(bt);
if (t === void 0)
throw new Error("useLayoutContext must be used within a LayoutProvider");
return t;
}
const Vt = (t) => {
switch (t) {
case "DOWN":
return "TB";
case "RIGHT":
return "LR";
case "UP":
return "BT";
case "LEFT":
return "RL";
default:
return "TB";
}
}, ut = (t) => {
switch (t) {
case "TB":
return "DOWN";
case "LR":
return "RIGHT";
case "BT":
return "UP";
case "RL":
return "LEFT";
default:
return "DOWN";
}
}, Yt = {
enabled: !0,
padding: {
horizontal: 80,
vertical: 48
},
respectHeaderHeight: !0,
minWidth: 200,
minHeight: 100
}, jt = (t) => {
switch (t) {
case "DOWN":
return R.Bottom;
case "RIGHT":
return R.Right;
case "UP":
return R.Top;
case "LEFT":
return R.Left;
default:
return R.Bottom;
}
}, Xt = (t) => {
switch (t) {
case "DOWN":
return R.Top;
case "RIGHT":
return R.Left;
case "UP":
return R.Bottom;
case "LEFT":
return R.Right;
default:
return R.Top;
}
};
function Jt(t, r, e) {
if (e)
return { nodes: t, edges: r };
const s = t.filter((l) => !l.hidden), n = new Set(s.map((l) => l.id)), o = r.filter(
(l) => n.has(l.source) && n.has(l.target)
);
return { nodes: s, edges: o };
}
const Wt = 172, Zt = 36, Y = async (t, r, e, s = 0, n = 50, o = 50, l = Wt, a = Zt, h = !1) => {
const c = new dt.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
c.setGraph({
rankdir: e,
marginx: s,
marginy: s,
nodesep: n,
ranksep: o,
edgesep: Math.max(20, n / 4),
//When we have siblings, we want their top edges to be aligned
ranker: "tight-tree"
});
const i = h ? t : t.filter((p) => !p.hidden);
if (i.length === 0)
return { nodes: [], edges: [], width: 0, height: 0 };
i.forEach((p) => {
var y, w;
const f = Number((y = p.style) == null ? void 0 : y.width) || l, b = Number((w = p.style) == null ? void 0 : w.height) || a;
c.setNode(p.id, { width: f, height: b });
}), r.forEach((p) => {
var E, C;
const f = p.sourceHandle, b = p.targetHandle, y = !!((E = p.data) != null && E.isReciprocal), w = !!((C = p.data) != null && C.isSyntheticBridge);
let g = {};
y ? g = { constraint: !1, minlen: 1, weight: 5 } : w ? g = { constraint: !0, minlen: 1, weight: 4 } : f === "right" && b === "left" ? g = { constraint: !1, minlen: 1 } : f === "bottom" && b === "top" ? g = { constraint: !0, minlen: 1 } : g = { constraint: !0, minlen: 2 }, c.setEdge(p.source, p.target, g);
}), dt.layout(c);
const u = c.graph().width || 0, x = c.graph().height || 0, v = jt(ut(e)), m = Xt(ut(e));
return {
nodes: i.map((p) => {
var C;
const f = c.node(p.id), { width: b, height: y } = f, w = {
...p,
sourcePosition: v,
targetPosition: m,
selected: !1
}, g = f.x, E = f.y;
switch ((C = p.data) == null ? void 0 : C.positionType) {
case "center":
w.position = { x: g, y: E };
break;
case "topRight":
w.position = { x: g + b / 2, y: E - y / 2 };
break;
case "bottomLeft":
w.position = { x: g - b / 2, y: E + y / 2 };
break;
case "bottomRight":
w.position = { x: g + b / 2, y: E + y / 2 };
break;
case "topLeft":
default:
w.position = { x: g - b / 2, y: E - y / 2 };
}
return w;
}),
edges: r,
width: u,
height: x
};
}, qt = {
calculate: async (t, r, e) => {
const s = e.nodes || t, n = e.edges || r, l = e.dagreDirection || typeof e.direction == "string" && (e.direction === "RIGHT" || e.direction === "LEFT" ? "LR" : "TB") || "TB", a = e.margin ?? (typeof e.padding == "number" ? e.padding : 0), h = e.nodeSpacing ?? 50, c = e.layerSpacing ?? 50, i = e.nodeWidth ?? 172, u = e.nodeHeight ?? 36, x = e.layoutHidden ?? !1;
return Y(
s,
n,
l,
a,
h,
c,
i,
u,
x
);
}
}, Qt = {
dagre: qt
};
function Kt({
initialDirection: t = "DOWN",
initialAlgorithm: r = "layered",
initialAutoLayout: e = !0,
initialPadding: s = 50,
initialSpacing: n = { node: 50, layer: 50 },
initialNodeDimensions: o = { width: 172, height: 36 },
initialParentResizingOptions: l,
includeHidden: a = !1,
layoutEngines: h
}) {
const [c, i] = k(t), [u, x] = k(r), [v, m] = k(e), [L, p] = k(!1), [f, b] = k(a), [y, w] = k({
...Qt,
...h
}), [g, E] = k({}), [C, B] = k(s), [$, T] = k(n.node || 150), [S, O] = k(n.layer || 180), [F, P] = k(o.width || 100), [j, X] = k(o.height || 100), [J, W] = k({
...Yt,
...l,
enabled: e
}), [U, Z] = k([]);
return {
// State
direction: c,
algorithm: u,
autoLayout: v,
layoutInProgress: L,
layoutHidden: f,
layoutEngines: y,
layoutEngineOptions: g,
padding: C,
nodeSpacing: $,
layerSpacing: S,
nodeWidth: F,
nodeHeight: j,
parentResizingOptions: J,
selectedNodes: U,
// Setters
setDirection: i,
setAlgorithm: x,
setAutoLayout: m,
setLayoutInProgress: p,
setLayoutHidden: b,
setLayoutEngines: w,
setLayoutEngineOptions: E,
setPadding: B,
setNodeSpacing: T,
setLayerSpacing: O,
setNodeWidth: P,
setNodeHeight: X,
setParentResizingOptionsState: W,
setSelectedNodes: Z
};
}
function te({
nodes: t,
externalNodeIdWithNode: r,
externalNodeParentIdMapWithChildIdSet: e,
noParentKey: s = "no-parent"
}) {
const [n, o] = k(/* @__PURE__ */ new Map()), [l, a] = k(/* @__PURE__ */ new Map()), [h, c] = k({}), [i, u] = k(t.length), [x, v] = k(!1), m = r || n, L = e || l, p = m.size;
return I(() => {
if (!r || !e) {
const f = /* @__PURE__ */ new Map(), b = /* @__PURE__ */ new Map();
t.forEach((y) => {
var g;
f.set(y.id, y);
const w = y.parentId || s;
b.has(w) || b.set(w, /* @__PURE__ */ new Set()), (g = b.get(w)) == null || g.add(y.id);
}), r || o(f), e || a(b);
}
}, [t, r, e, s]), I(() => {
t.length !== i && u(t.length);
}, [t]), I(() => {
m.size > 0 && L.size > 0 && !x && v(!0);
}, [m, L, x]), I(() => {
const f = {};
L.forEach((y, w) => {
f[w] = y.size;
});
let b = !1;
for (const y in f)
if (!h.hasOwnProperty(y) || f[y] !== h[y]) {
b = !0;
break;
}
b && c(f);
}, [L]), {
nodeIdWithNode: m,
nodeParentIdMapWithChildIdSet: L,
numberOfNodes: p,
nodesLength: i,
childrenInitialized: x,
parentChildStructure: h
};
}
function ee({
nodes: t,
edges: r,
selectedNodes: e,
layoutHidden: s,
calculateLayout: n,
updateNodes: o,
updateEdges: l,
setLayoutInProgress: a,
setNodeSpacing: h,
setLayerSpacing: c
}) {
const i = _t(), u = ht(null), x = ht(null);
return {
applyLayout: G(async (m = [], L = []) => {
u.current && u.current.abort();
const p = new AbortController();
u.current = p;
const f = m.length > 0 ? m : t, b = L.length > 0 ? L : r;
if (f.length === 0)
return { nodes: f, edges: b };
try {
if (a(!0), p.signal.aborted)
return { nodes: f, edges: b };
const { nodes: y, edges: w } = Jt(f, b, s), g = await n(y, w, e, p.signal);
return p.signal.aborted ? { nodes: f, edges: b } : (u.current === p && (o ? o(g.nodes) : i != null && i.setNodes && i.setNodes(g.nodes), l ? l(g.edges) : i != null && i.setEdges && i.setEdges(g.edges), x.current && (x.current.node !== void 0 && h(x.current.node), x.current.layer !== void 0 && c(x.current.layer), x.current = null)), g);
} catch (y) {
return p.signal.aborted || console.error("Error applying layout:", y), { nodes: f, edges: b };
} finally {
u.current === p && (a(!1), u.current = null);
}
}, [
t,
r,
e,
n,
o,
l,
i,
s,
a,
h,
c
]),
pendingSpacingUpdateRef: x
};
}
const re = (t, r, e = "no-parent") => {
const s = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Map();
t.forEach((a, h) => {
const c = r.get(h);
c && n.set(h, {
id: h,
node: c,
children: [],
depth: 0
});
});
const o = (a, h) => {
if (s.has(a) || !t.has(a) || !r.get(a)) return null;
s.add(a);
const i = n.get(a);
i.depth = h;
const u = t.get(a) || /* @__PURE__ */ new Set();
for (const x of u)
if (t.has(x) && r.get(x)) {
const m = o(x, h + 1);
m && i.children.push(m);
}
return i;
}, l = [];
return t.forEach((a, h) => {
const c = r.get(h);
if (c) {
const i = c.parentId;
if (!i || i === e) {
const u = o(h, 0);
u && l.push(u);
}
}
}), l;
};
function K(t, r, e = "no-parent") {
const s = [];
let n = t;
const o = /* @__PURE__ */ new Set();
for (; n && n !== e; ) {
if (o.has(n)) {
console.warn(`Cycle detected in ancestor path for node ${t}`);
break;
}
o.add(n), s.push(n);
const l = r.get(n);
if (!l || !l.parentId) {
n = e;
break;
}
n = l.parentId;
}
return n === e && s.push(e), s;
}
function D(t, r, e, s = "no-parent") {
if (t === r)
return t;
const n = K(t, e, s), o = n.indexOf(r);
return o === -1 ? null : o === 0 ? t : n[o - 1] || null;
}
function H(t, r, e, s = "no-parent") {
const n = K(t, e, s), o = K(r, e, s), l = new Set(o);
let a = null, h = -1;
for (let x = 0; x < n.length; x++)
if (l.has(n[x])) {
a = n[x], h = x;
break;
}
if (!a)
return { lca: null, sourceChild: null, targetChild: null };
const c = o.indexOf(a), i = h > 0 ? n[h - 1] : t, u = c > 0 ? o[c - 1] : r;
return { lca: a, sourceChild: i, targetChild: u };
}
const ne = (t) => JSON.parse(JSON.stringify(t)), Lt = (t, r, e, s, n) => {
const o = ne(t);
return o.source = r, o.target = e, o.id = s, o.data = {
...o.data,
...n
}, o;
}, se = (t) => {
for (const r of t.values()) {
const e = /* @__PURE__ */ new Map();
r.forEach((n) => {
const o = `${n.source}->${n.target}`, l = e.get(o) || [];
l.push(n), e.set(o, l);
});
const s = /* @__PURE__ */ new Set();
r.forEach((n) => {
const o = `${n.source}->${n.target}`, l = `${n.target}->${n.source}`;
if (s.has(o) || s.has(l))
return;
const a = e.get(l);
if (!a || a.length === 0)
return;
[...e.get(o) || [], ...a].forEach((c) => {
c.data = {
...c.data,
isReciprocal: !0
};
}), s.add(o), s.add(l);
});
}
}, ie = (t, r, e, s) => {
const n = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map();
t.forEach((a) => {
const h = n.get(a.target) || [];
h.push(a), n.set(a.target, h);
const c = o.get(a.source) || [];
c.push(a), o.set(a.source, c);
});
const l = /* @__PURE__ */ new Set();
o.forEach((a, h) => {
const c = n.get(h) || [];
c.length !== 0 && c.forEach((i) => {
a.forEach((u) => {
if (i.source === u.target)
return;
const { lca: x, sourceChild: v, targetChild: m } = H(
i.source,
u.target,
r,
s
), L = H(
i.source,
h,
r,
s
).lca, p = H(
h,
u.target,
r,
s
).lca;
if (!L || L !== p)
return;
const f = D(
i.source,
L,
r,
s
), b = D(
u.target,
L,
r,
s
), y = D(
h,
L,
r,
s
);
if (!f || !b || !y || f !== b || f === y)
return;
const w = f, g = D(
i.source,
w,
r,
s
), E = D(
u.target,
w,
r,
s
);
if (!x || !v || !m || !g || !E || g === E)
return;
const C = `synthetic_bridge_${i.id}_${h}_${u.id}_${w}`;
if (l.has(C))
return;
const B = Lt(
i,
g,
E,
C,
{
isTemporary: !0,
isSyntheticBridge: !0,
bridgeNode: h,
bridgeBranch: y,
anchorBranch: f,
originalSource: i.source,
originalTarget: u.target
}
);
e.has(w) || e.set(w, []), e.get(w).push(B), l.add(C);
});
});
});
};
function Ct(t, r, e = "no-parent") {
const s = /* @__PURE__ */ new Map();
for (const n of t) {
const { lca: o, sourceChild: l, targetChild: a } = H(
n.source,
n.target,
r,
e
);
if (o && l && a && l !== a) {
const h = Lt(
n,
l,
a,
`temp_${n.id}_${o}`,
{
isTemporary: !0,
originalSource: n.source,
originalTarget: n.target
}
);
s.has(o) || s.set(o, []), s.get(o).push(h);
}
}
return ie(t, r, s, e), se(s), s;
}
const et = 10, pt = (t, r) => t.find((e) => e.id === r), gt = (t, r, e) => {
var a, h;
const s = e.get(t);
if (!s)
return null;
let n = s.position.x + (s.width || Number((a = s.style) == null ? void 0 : a.width) || 172) / 2, o = s.position.y + (s.height || Number((h = s.style) == null ? void 0 : h.height) || 36) / 2, l = s.parentId;
for (; l && l !== r; ) {
const c = e.get(l);
if (!c)
break;
n += c.position.x, o += c.position.y, l = c.parentId;
}
return { x: n, y: o };
}, z = (t) => {
var r, e;
return {
width: t.width || Number((r = t.style) == null ? void 0 : r.width) || 172,
height: t.height || Number((e = t.style) == null ? void 0 : e.height) || 36
};
}, ft = (t) => {
const { width: r, height: e } = z(t);
return {
x: t.position.x + r / 2,
y: t.position.y + e / 2
};
}, oe = (t, r) => {
if (t.length === 0)
return { nodes: t, width: 0, height: 0 };
let e = Number.POSITIVE_INFINITY, s = Number.POSITIVE_INFINITY, n = Number.NEGATIVE_INFINITY, o = Number.NEGATIVE_INFINITY;
t.forEach((i) => {
const { width: u, height: x } = z(i);
e = Math.min(e, i.position.x), s = Math.min(s, i.position.y), n = Math.max(n, i.position.x + u), o = Math.max(o, i.position.y + x);
});
const l = r - e, a = r - s;
t.forEach((i) => {
i.position = {
x: i.position.x + l,
y: i.position.y + a
};
});
const h = n - e + r * 2, c = o - s + r * 2;
return { nodes: t, width: h, height: c };
}, ce = (t, r, e, s) => {
if (!(t.length < 2 || s.size === 0))
if (r === "TB" || r === "BT") {
const n = [...t].sort((o, l) => o.position.x - l.position.x);
n.forEach((o, l) => {
if (!s.has(o.id))
return;
let a = o.position.x + z(o).width;
for (let h = l + 1; h < n.length; h++) {
const c = n[h], { width: i } = z(c), u = a + e;
c.position.x < u && (c.position.x = u), a = c.position.x + i;
}
});
} else {
const n = [...t].sort((o, l) => o.position.y - l.position.y);
n.forEach((o, l) => {
if (!s.has(o.id))
return;
let a = o.position.y + z(o).height;
for (let h = l + 1; h < n.length; h++) {
const c = n[h], { height: i } = z(c), u = a + e;
c.position.y < u && (c.position.y = u), a = c.position.y + i;
}
});
}
}, ae = (t, r, e, s) => {
const n = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map();
r.forEach((a) => {
const h = n.get(a.target) || [];
h.push(a), n.set(a.target, h);
const c = o.get(a.source) || [];
c.push(a), o.set(a.source, c);
});
const l = /* @__PURE__ */ new Map();
return o.forEach((a, h) => {
const c = n.get(h) || [];
c.length !== 0 && c.forEach((i) => {
a.forEach((u) => {
if (i.source === u.target)
return;
const x = H(
i.source,
h,
e,
s
).lca, v = H(
h,
u.target,
e,
s
).lca;
if (x !== t || v !== t)
return;
const m = D(
i.source,
t,
e,
s
), L = D(
u.target,
t,
e,
s
), p = D(
h,
t,
e,
s
);
if (!m || !L || !p || m !== L || m === p)
return;
const f = D(
i.source,
m,
e,
s
), b = D(
u.target,
m,
e,
s
);
if (!f || !b || f === b)
return;
const y = `${m}:${p}:${f}:${b}`;
l.set(y, {
anchorContainerId: m,
bridgeContainerId: p,
sourceNodeId: f,
targetNodeId: b
});
});
});
}), [...l.values()];
}, le = (t, r, e, s, n, o, l) => {
const a = ae(r, n, o, l), h = /* @__PURE__ */ new Set();
return a.forEach((c) => {
const i = pt(t, c.anchorContainerId), u = pt(t, c.bridgeContainerId);
if (!i || !u)
return;
const x = gt(c.sourceNodeId, r, o), v = gt(c.targetNodeId, r, o);
if (!x || !v)
return;
const { width: m, height: L } = z(u), { width: p, height: f } = z(i), b = ft(i), y = ft(u);
if (e === "TB" || e === "BT") {
const w = (x.y + v.y) / 2;
u.position.y = w - L / 2;
const g = y.x >= b.x, E = g ? u.position.x - (i.position.x + p) : i.position.x - (u.position.x + m), C = Math.max(s, E);
u.position.x = g ? i.position.x + p + C : i.position.x - m - C;
} else {
const w = (x.x + v.x) / 2;
u.position.x = w - m / 2;
const g = y.y >= b.y, E = g ? u.position.y - (i.position.y + f) : i.position.y - (u.position.y + L), C = Math.max(s, E);
u.position.y = g ? i.position.y + f + C : i.position.y - L - C;
}
h.add(u.id);
}), h;
}, Et = async (t, r, e, s, n, o = et, l = 50, a = 50, h = 172, c = 36, i = Y, u = !1, x = "no-parent") => {
const v = Ct(n, s, x), { updatedNodes: m } = await tt(
t,
r,
e,
s,
n,
o,
l,
a,
h,
c,
i,
u,
v,
x
), L = s.get(t);
if (!L)
return { updatedNodes: m, updatedEdges: n };
const { updatedNodes: p } = await Et(
L.parentId || x,
r,
e,
s,
n,
o,
l,
a,
h,
c,
i,
u,
x
);
return {
updatedNodes: [...p, ...m],
updatedEdges: n
// Always return original edges unchanged
};
}, tt = async (t, r, e, s, n, o = et, l = 50, a = 50, h = 172, c = 36, i = Y, u = !1, x = /* @__PURE__ */ new Map(), v = "no-parent") => {
const m = e.get(t);
if (!m || m.size === 0)
return { updatedNodes: [] };
const L = [];
if (m.forEach((C) => {
const B = s.get(C);
B && L.push(B);
}), L.length === 0)
return { updatedNodes: [] };
let p = r;
const f = s.get(t);
f && f.data.layoutDirection && (p = f.data.layoutDirection);
const b = x.get(t) || [], { nodes: y, edges: w } = await i(
L,
b,
// Only use temporary edges for layout
p,
o,
l,
a,
h,
c,
u
);
y.forEach((C) => {
s.set(C.id, C);
});
const g = le(
y,
t,
p,
l,
n,
s,
v
);
ce(
y,
p,
l,
g
), y.forEach((C) => {
s.set(C.id, C);
});
const E = oe(y, o);
return E.nodes.forEach((C) => {
s.set(C.id, C);
}), f && E.width && E.height && he(f, E.width, E.height), {
updatedNodes: [...E.nodes],
udpatedParentNode: f || void 0
};
}, he = (t, r, e) => (t.style || (t.style = {}), t.width = r, t.height = e, t.measured = {
width: r,
height: e
}, t.style.width = r, t.style.height = e, t), de = async (t, r, e, s, n, o = et, l = 50, a = 50, h = 172, c = 36, i = Y, u = !1, x = "no-parent") => {
let v = [];
const m = Ct(n, s, x), L = /* @__PURE__ */ new Map();
let p = 0;
const f = (y, w) => {
w > p && (p = w);
for (const g of y)
L.has(w) || L.set(w, []), L.get(w).push(g.id), g.children.length > 0 && f(g.children, w + 1);
};
f(t, 0);
for (let y = p; y >= 0; y--) {
const w = L.get(y) || [], g = await Promise.all(w.map(
(E) => tt(
E,
r,
e,
s,
n,
// Pass original edges (only used for returning)
o,
l,
a,
h,
c,
i,
u,
m,
// Pass the global temporary edges map
x
)
));
for (const { updatedNodes: E } of g)
v = [...E, ...v];
}
const { updatedNodes: b } = await tt(
x,
r,
e,
s,
n,
// Pass original edges (only used for returning)
o,
l,
a,
h,
c,
i,
u,
m,
// Process root-level temporary edges
x
);
return v = [...b, ...v], {
updatedNodes: v,
updatedEdges: n
// Always return original edges unchanged
};
}, ue = (t, r, e, s = "no-parent") => {
const n = t.map((c) => c.id), o = t.map((c) => {
var i;
return ((i = e.get(c.id)) == null ? void 0 : i.parentId) || c.parentId;
}).filter((c) => !!c), a = Array.from(
/* @__PURE__ */ new Set([...n, ...o])
).filter((c) => r.has(c)), h = a.filter((c) => {
const i = e.get(c);
return i ? !i.parentId || !a.includes(i.parentId) : !0;
});
return h.length === 0 ? [s] : h;
}, pe = async (t, r, e) => {
const {
dagreDirection: s,
nodeParentIdMapWithChildIdSet: n,
nodeIdWithNode: o,
nodes: l,
edges: a,
margin: h,
nodeSpacing: c,
layerSpacing: i,
nodeWidth: u,
nodeHeight: x,
layoutHidden: v = !1,
noParentKey: m = "no-parent"
} = r, L = ue(
t,
n,
o,
m
);
if (L.length === 0)
return { nodes: l, edges: a };
const p = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Map();
if (e != null && e.aborted)
return { nodes: l, edges: a };
(await Promise.all(
L.map(async (g) => e != null && e.aborted ? { updatedNodes: [], updatedEdges: [] } : Et(
g,
s,
n,
o,
a,
h,
c,
i,
u,
x,
void 0,
v
))
)).forEach(({ updatedNodes: g, updatedEdges: E }) => {
g.forEach((C) => {
p.set(C.id, C);
}), E.forEach((C) => {
f.set(C.id, C);
});
});
const y = l.map(
(g) => p.has(g.id) ? p.get(g.id) : g
), w = a.map(
(g) => f.has(g.id) ? f.get(g.id) : g
);
return { nodes: y, edges: w };
}, ge = (t, r, e, s, n, o, l, a, h = 172, c = 36, i = !1, u = "no-parent") => ({ calculateLayout: G(async (v, m, L, p) => {
if (!(t[e] || t.dagre))
return console.error(`Layout engine "${e}" not found`), { nodes: v, edges: m };
const b = i ? v : v.filter((T) => !T.hidden), y = new Set(b.map((T) => T.id)), w = i ? m : m.filter(
(T) => y.has(T.source) && y.has(T.target)
), g = Vt(r), E = s.padding.horizontal;
let C = [], B = [];
if (L && L.length > 0) {
const T = i ? L : L.filter((O) => !O.hidden), S = await pe(
T,
{
dagreDirection: g,
nodeParentIdMapWithChildIdSet: n,
nodeIdWithNode: o,
nodes: b,
edges: w,
margin: E,
nodeSpacing: l,
layerSpacing: a,
nodeWidth: h,
nodeHeight: c,
layoutHidden: i,
noParentKey: u
},
p
);
C = S.nodes, B = S.edges;
} else {
if (p != null && p.aborted)
return { nodes: v, edges: m };
const T = re(n, o, u), S = await de(
T,
g,
n,
o,
w,
E,
l,
a,
h,
c,
void 0,
i,
u
);
C = S.updatedNodes, B = S.updatedEdges;
}
return {
nodes: v.map((T) => !i && T.hidden ? T : C.find((O) => O.id === T.id) || T),
edges: B
};
}, [
e,
r,
t,
s.padding.horizontal,
n,
o,
l,
a,
h,
c,
i,
u
]) });
function me({
children: t,
initialDirection: r = "DOWN",
initialAlgorithm: e = "layered",
initialAutoLayout: s = !0,
initialPadding: n = 50,
initialSpacing: o = { node: 50, layer: 50 },
initialNodeDimensions: l = { width: 172, height: 36 },
initialParentResizingOptions: a,
includeHidden: h = !1,
layoutEngines: c,
updateNodes: i,
updateEdges: u,
nodeParentIdMapWithChildIdSet: x,
nodeIdWithNode: v,
noParentKey: m = "no-parent",
disableAutoLayoutEffect: L = !1
}) {
const p = $t(), f = Ft(), {
direction: b,
algorithm: y,
autoLayout: w,
layoutInProgress: g,
layoutHidden: E,
layoutEngines: C,
layoutEngineOptions: B,
padding: $,
nodeSpacing: T,
layerSpacing: S,
nodeWidth: O,
nodeHeight: F,
parentResizingOptions: P,
selectedNodes: j,
setDirection: X,
setAlgorithm: J,
setAutoLayout: W,
setLayoutInProgress: U,
setLayoutHidden: Z,
setLayoutEngines: rt,
setLayoutEngineOptions: vt,
setPadding: Nt,
setNodeSpacing: nt,
setLayerSpacing: st,
setNodeWidth: Tt,
setNodeHeight: kt,
setParentResizingOptionsState: it,
setSelectedNodes: ot
} = Kt({
initialDirection: r,
initialAlgorithm: e,
initialAutoLayout: s,
initialPadding: n,
initialSpacing: o,
initialNodeDimensions: l,
initialParentResizingOptions: a,
includeHidden: h,
layoutEngines: c
}), {
nodeIdWithNode: q,
nodeParentIdMapWithChildIdSet: ct,
numberOfNodes: Bt,
nodesLength: St,
childrenInitialized: at,
parentChildStructure: Mt
} = te({
nodes: p,
externalNodeIdWithNode: v,
externalNodeParentIdMapWithChildIdSet: x,
noParentKey: m
}), Rt = G(({ nodes: A }) => {
ot(A);
}, [ot]);
Pt({
onChange: Rt
});
const { calculateLayout: Dt } = ge(
C,
b,
y,
P,
ct,
q,
T,
S,
O,
F,
E,
m
), { applyLayout: lt } = ee({
nodes: p,
edges: f,
selectedNodes: j,
layoutHidden: E,
calculateLayout: Dt,
updateNodes: i,
updateEdges: u,
setLayoutInProgress: U,
setNodeSpacing: nt,
setLayerSpacing: st
}), Ot = G((A, Q) => {
rt((Ht) => ({ ...Ht, [A]: Q }));
}, [rt]), At = G((A) => {
it((Q) => ({
...Q,
...A,
enabled: w
}));
}, [w, it]);
I(() => {
var A;
L || q.has((A = p[p.length - 1]) == null ? void 0 : A.id) && at && w && lt();
}, [at, w, b, Bt, T, S, Mt, St]);
const zt = {
direction: b,
algorithm: y,
autoLayout: w,
layoutInProgress: g,
padding: $,
nodeSpacing: T,
layerSpacing: S,
nodeWidth: O,
nodeHeight: F,
layoutHidden: E,
parentResizingOptions: P,
layoutEngines: C,
layoutEngineOptions: B,
nodeParentIdMapWithChildIdSet: ct,
nodeIdWithNode: q,
noParentKey: m,
updateNodes: i,
updateEdges: u,
setDirection: X,
setAlgorithm: J,
setAutoLayout: W,
setLayoutInProgress: U,
setPadding: Nt,
setNodeSpacing: nt,
setLayerSpacing: st,
setNodeWidth: Tt,
setNodeHeight: kt,
setLayoutHidden: Z,
setParentResizingOptions: At,
setLayoutEngineOptions: vt,
applyLayout: lt,
clearLayoutCache: () => {
},
registerLayoutEngine: Ot
};
return /* @__PURE__ */ d(bt.Provider, { value: zt, children: t });
}
const yt = ({
compact: t = !1
}) => {
const {
direction: r,
setDirection: e,
clearLayoutCache: s
} = _(), n = (o) => {
e(o), s && s();
};
return t ? /* @__PURE__ */ d(
M,
{
onClick: () => n(r === "DOWN" ? "RIGHT" : "DOWN"),
title: `Switch to ${r === "DOWN" ? "horizontal" : "vertical"} layout`,
children: /* @__PURE__ */ d("svg", { viewBox: "0 0 24 24", width: "16", height: "16", stroke: "currentColor", strokeWidth: "2", fill: "none", children: r === "DOWN" ? (
// Horizontal layout icon
/* @__PURE__ */ d("path", { d: "M4 12h16M16 6l6 6-6 6" })
) : (
// Vertical layout icon
/* @__PURE__ */ d("path", { d: "M12 4v16M6 16l6 6 6-6" })
) })
}
) : /* @__PURE__ */ N(V, { children: [
/* @__PURE__ */ d(
M,
{
onClick: () => n("DOWN"),
className: r === "DOWN" ? "selected" : "",
title: "Top to Bottom",
children: /* @__PURE__ */ d("svg", { viewBox: "0 0 24 24", width: "16", height: "16", stroke: "currentColor", strokeWidth: "2", fill: "none", children: /* @__PURE__ */ d("path", { d: "M12 4v16M6 16l6 6 6-6" }) })
}
),
/* @__PURE__ */ d(
M,
{
onClick: () => n("RIGHT"),
className: r === "RIGHT" ? "selected" : "",
title: "Left to Right",
children: /* @__PURE__ */ d("svg", { viewBox: "0 0 24 24", width: "16", height: "16", stroke: "currentColor", strokeWidth: "2", fill: "none", children: /* @__PURE__ */ d("path", { d: "M4 12h16M16 6l6 6-6 6" }) })
}
),
/* @__PURE__ */ d(
M,
{
onClick: () => n("LEFT"),
className: r === "LEFT" ? "selected" : "",
title: "Right to Left",
children: /* @__PURE__ */ d("svg", { viewBox: "0 0 24 24", width: "16", height: "16", stroke: "currentColor", strokeWidth: "2", fill: "none", children: /* @__PURE__ */ d("path", { d: "M20 12H4M8 6L2 12l6 6" }) })
}
),
/* @__PURE__ */ d(
M,
{
onClick: () => n("UP"),
className: r === "UP" ? "selected" : "",
title: "Bottom to Top",
children: /* @__PURE__ */ d("svg", { viewBox: "0 0 24 24", width: "16", height: "16", stroke: "currentColor", strokeWidth: "2", fill: "none", children: /* @__PURE__ */ d("path", { d: "M12 20V4M6 8l6-6 6 6" }) })
}
)
] });
}, xt = ({
compact: t = !1
}) => {
const {
nodeSpacing: r,
layerSpacing: e,
setNodeSpacing: s,
setLayerSpacing: n,
clearLayoutCache: o
} = _(), [l, a] = k(!1), h = (i) => {
s(i), o && o();
}, c = (i) => {
n(i), o && o();
};
return t ? /* @__PURE__ */ N("div", { style: { position: "relative" }, children: [
/* @__PURE__ */ d(
M,
{
onClick: () => a(!l),
title: "Adjust node and layer spacing",
children: /* @__PURE__ */ N("svg", { viewBox: "0 0 24 24", width: "16", height: "16", stroke: "currentColor", strokeWidth: "2", fill: "none", children: [
/* @__PURE__ */ d("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "14", y: "3", width: "7", height: "7", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "3", y: "14", width: "7", height: "7", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "14", y: "14", width: "7", height: "7", rx: "1" }),
/* @__PURE__ */ d("path", { d: "M7 10v4M17 10v4M10 7h4M10 17h4" })
] })
}
),
l && /* @__PURE__ */ N(
"div",
{
className: "react-flow-spacing-dropdown",
style: {
position: "absolute",
right: "0",
top: "100%",
marginTop: "8px",
background: "white",
border: "1px solid #ddd",
borderRadius: "4px",
padding: "8px",
boxShadow: "0 2px 10px rgba(0,0,0,0.1)",
zIndex: 10,
minWidth: "200px"
},
children: [
/* @__PURE__ */ N("div", { style: { marginBottom: "12px" }, children: [
/* @__PURE__ */ N("div", { style: { fontWeight: "bold", marginBottom: "4px" }, children: [
"Node Spacing: ",
r,
"px"
] }),
/* @__PURE__ */ d("div", { style: { display: "flex", gap: "4px" }, children: [50, 100, 150, 200].map((i) => /* @__PURE__ */ d(
"button",
{
onClick: () => h(i),
style: {
background: r === i ? "#0041d0" : "#f5f5f5",
color: r === i ? "white" : "inherit",
border: "1px solid #ddd",
borderRadius: "4px",
padding: "4px 8px",
cursor: "pointer"
},
children: i
},
i
)) })
] }),
/* @__PURE__ */ N("div", { children: [
/* @__PURE__ */ N("div", { style: { fontWeight: "bold", marginBottom: "4px" }, children: [
"Layer Spacing: ",
e,
"px"
] }),
/* @__PURE__ */ d("div", { style: { display: "flex", gap: "4px" }, children: [50, 100, 150, 200].map((i) => /* @__PURE__ */ d(
"button",
{
onClick: () => c(i),
style: {
background: e === i ? "#0041d0" : "#f5f5f5",
color: e === i ? "white" : "inherit",
border: "1px solid #ddd",
borderRadius: "4px",
padding: "4px 8px",
cursor: "pointer"
},
children: i
},
i
)) })
] }),
/* @__PURE__ */ d("div", { style: { marginTop: "12px", textAlign: "right" }, children: /* @__PURE__ */ d(
"button",
{
onClick: () => a(!1),
style: {
background: "#f5f5f5",
border: "1px solid #ddd",
borderRadius: "4px",
padding: "4px 8px",
cursor: "pointer"
},
children: "Close"
}
) })
]
}
)
] }) : /* @__PURE__ */ N("div", { className: "spacing-controls", children: [
/* @__PURE__ */ N(
M,
{
onClick: () => h(Math.max(50, r - 25)),
title: "Decrease node spacing",
children: [
/* @__PURE__ */ N("svg", { viewBox: "0 0 24 24", width: "16", height: "16", stroke: "currentColor", strokeWidth: "2", fill: "none", children: [
/* @__PURE__ */ d("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "14", y: "3", width: "7", height: "7", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "3", y: "14", width: "7", height: "7", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "14", y: "14", width: "7", height: "7", rx: "1" }),
/* @__PURE__ */ d("line", { x1: "7", y1: "10", x2: "7", y2: "14" }),
/* @__PURE__ */ d("line", { x1: "17", y1: "10", x2: "17", y2: "14" }),
/* @__PURE__ */ d("line", { x1: "10", y1: "7", x2: "14", y2: "7" }),
/* @__PURE__ */ d("line", { x1: "10", y1: "17", x2: "14", y2: "17" })
] }),
/* @__PURE__ */ d("span", { style: { marginLeft: "4px" }, children: "-" })
]
}
),
/* @__PURE__ */ N("div", { style: {
display: "inline-flex",
alignItems: "center",
padding: "5px 10px",
fontSize: "12px",
background: "#f8f8f8",
borderRadius: "4px",
margin: "0 4px",
border: "1px solid #ddd"
}, children: [
r,
"px"
] }),
/* @__PURE__ */ N(
M,
{
onClick: () => h(Math.min(300, r + 25)),
title: "Increase node spacing",
children: [
/* @__PURE__ */ N("svg", { viewBox: "0 0 24 24", width: "16", height: "16", stroke: "currentColor", strokeWidth: "2", fill: "none", children: [
/* @__PURE__ */ d("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "14", y: "3", width: "7", height: "7", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "3", y: "14", width: "7", height: "7", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "14", y: "14", width: "7", height: "7", rx: "1" }),
/* @__PURE__ */ d("line", { x1: "7", y1: "10", x2: "7", y2: "14" }),
/* @__PURE__ */ d("line", { x1: "17", y1: "10", x2: "17", y2: "14" }),
/* @__PURE__ */ d("line", { x1: "10", y1: "7", x2: "14", y2: "7" }),
/* @__PURE__ */ d("line", { x1: "10", y1: "17", x2: "14", y2: "17" })
] }),
/* @__PURE__ */ d("span", { style: { marginLeft: "4px" }, children: "+" })
]
}
),
/* @__PURE__ */ d("div", { style: { display: "inline-block", width: "1px", height: "24px", background: "#ddd", margin: "0 10px" } }),
/* @__PURE__ */ N(
M,
{
onClick: () => c(Math.max(50, e - 25)),
title: "Decrease layer spacing",
children: [
/* @__PURE__ */ N("svg", { viewBox: "0 0 24 24", width: "16", height: "16", stroke: "currentColor", strokeWidth: "2", fill: "none", children: [
/* @__PURE__ */ d("line", { x1: "4", y1: "6", x2: "20", y2: "6" }),
/* @__PURE__ */ d("line", { x1: "4", y1: "12", x2: "20", y2: "12" }),
/* @__PURE__ */ d("line", { x1: "4", y1: "18", x2: "20", y2: "18" })
] }),
/* @__PURE__ */ d("span", { style: { marginLeft: "4px" }, children: "-" })
]
}
),
/* @__PURE__ */ N("div", { style: {
display: "inline-flex",
alignItems: "center",
padding: "5px 10px",
fontSize: "12px",
background: "#f8f8f8",
borderRadius: "4px",
margin: "0 4px",
border: "1px solid #ddd"
}, children: [
e,
"px"
] }),
/* @__PURE__ */ N(
M,
{
onClick: () => c(Math.min(300, e + 25)),
title: "Increase layer spacing",
children: [
/* @__PURE__ */ N("svg", { viewBox: "0 0 24 24", width: "16", height: "16", stroke: "currentColor", strokeWidth: "2", fill: "none", children: [
/* @__PURE__ */ d("line", { x1: "4", y1: "6", x2: "20", y2: "6" }),
/* @__PURE__ */ d("line", { x1: "4", y1: "12", x2: "20", y2: "12" }),
/* @__PURE__ */ d("line", { x1: "4", y1: "18", x2: "20", y2: "18" })
] }),
/* @__PURE__ */ d("span", { style: { marginLeft: "4px" }, children: "+" })
]
}
)
] });
}, wt = ({
compact: t = !1
}) => {
const {
autoLayout: r,
setAutoLayout: e
} = _();
return /* @__PURE__ */ d(
M,
{
onClick: () => {
e(!r);
},
className: r ? "selected" : "",
title: r ? "Disable automatic layout" : "Enable automatic layout",
style: { color: r ? "green" : "inherit" },
children: t ? /* @__PURE__ */ N("svg", { viewBox: "0 0 24 24", width: "16", height: "16", stroke: "currentColor", strokeWidth: "1.5", fill: "none", children: [
/* @__PURE__ */ d("rect", { x: "4", y: "4", width: "4", height: "4", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "4", y: "16", width: "4", height: "4", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "16", y: "10", width: "4", height: "4", rx: "1" }),
/* @__PURE__ */ N(V, { children: [
/* @__PURE__ */ d("path", { d: "M8 6c6 0 3 8 8 6" }),
/* @__PURE__ */ d("path", { d: "M8 18c3-3 2-8 6-6" })
] })
] }) : /* @__PURE__ */ N(V, { children: [
"Auto-Layout ",
r ? "ON" : "OFF"
] })
}
);
}, mt = ({
compact: t = !1
}) => {
const {
layoutInProgress: r,
setLayoutInProgress: e,
applyLayout: s
} = _();
return /* @__PURE__ */ N(
M,
{
onClick: async () => {
if (!r)
try {
e(!0), await s();
} catch (o) {
console.error("Error applying layout:", o);
}
},
title: "Apply layout",
disabled: r,
style: { color: r ? "#999" : "#000" },
children: [
/* @__PURE__ */ N("svg", { viewBox: "0 0 24 24", width: "16", height: "16", stroke: "currentColor", strokeWidth: "1.5", fill: "none", children: [
/* @__PURE__ */ d("rect", { x: "3", y: "3", width: "5", height: "5", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "16", y: "3", width: "5", height: "5", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "3", y: "16", width: "5", height: "5", rx: "1" }),
/* @__PURE__ */ d("rect", { x: "16", y: "16", width: "5", height: "5", rx: "1" }),
/* @__PURE__ */ d("path", { d: "M8 5.5h8M5.5 8v8M18.5 8v8M8 18.5h8" }),
/* @__PURE__ */ d("circle", { cx: "12", cy: "12", r: "3" }),
/* @__PURE__ */ d("path", { d: "M11 10.5v3l2-1.5z", fill: "currentColor" })
] }),
!t && /* @__PURE__ */ d("span", { style: { marginLeft: "4px" }, children: "Apply Layout" })
]
}
);
}, be = ({
showDirectionControls: t = !0,
showSpacingControls: r = !0,
showAutoLayoutToggle: e = !0,
showApplyLayoutButton: s = !0,
// Enable by default
standalone: n = !1,
position: o = "top-right"
}) => {
const l = _(), {
autoLayout: a
} = l;
return n ? /* @__PURE__ */ N(Ut, { position: o, showZoom: !1, showFitView: !1, showInteractive: !1, children: [
s && !a && /* @__PURE__ */ d(
mt,
{
compact: !0
}
),
t && /* @__PURE__ */ d(
yt,
{
compact: !0
}
),
r && /* @__PURE__ */ d(
xt,
{
compact: !0
}
),
e && /* @__PURE__ */ d(
wt,
{
compact: !0
}
)
] }) : /* @__PURE__ */ N(V, { children: [
s && !a && /* @__PURE__ */ d(
mt,
{
compact: !0
}
),
t && /* @__PURE__ */ d(
yt,
{
compact: !0
}
),
r && /* @__PURE__ */ d(
xt,
{
compact: !0
}
),
e && /* @__PURE__ */ d(
wt,
{
compact: !0
}
)
] });
};
export {
wt as AutoLayoutToggle,
qt as DagreEngine,
yt as DirectionControls,
be as LayoutControls,
me as LayoutProvider,
xt as SpacingControls,
Qt as engines,
_ as useLayoutContext
};