d3-graph-controller
Version:
A TypeScript library for visualizing and simulating directed, interactive graphs.
836 lines (835 loc) • 24.9 kB
JavaScript
var I = Object.defineProperty;
var j = (t, e, i) => e in t ? I(t, e, { enumerable: !0, configurable: !0, writable: !0, value: i }) : t[e] = i;
var d = (t, e, i) => j(t, typeof e != "symbol" ? e + "" : e, i);
import { debounce as M } from "@yeger/debounce";
import { select as w } from "d3-selection";
import { Vector as k } from "vecti";
import { zoomIdentity as E, zoom as U } from "d3-zoom";
import { drag as Z } from "d3-drag";
import { forceSimulation as W, forceX as H, forceY as V, forceManyBody as X, forceCollide as Y, forceLink as q } from "d3-force";
function P() {
return {
drag: {
end: 0,
start: 0.1
},
filter: {
link: 1,
type: 0.1,
unlinked: {
include: 0.1,
exclude: 0.1
}
},
focus: {
acquire: () => 0.1,
release: () => 0.1
},
initialize: 1,
labels: {
links: {
hide: 0,
show: 0
},
nodes: {
hide: 0,
show: 0
}
},
resize: 0.5
};
}
function N(t) {
if (typeof t == "object" && t !== null) {
if (typeof Object.getPrototypeOf == "function") {
const e = Object.getPrototypeOf(t);
return e === Object.prototype || e === null;
}
return Object.prototype.toString.call(t) === "[object Object]";
}
return !1;
}
function m(...t) {
return t.reduce((e, i) => {
if (Array.isArray(i))
throw new TypeError(
"Arguments provided to deepmerge must be objects, not arrays."
);
return Object.keys(i).forEach((n) => {
["__proto__", "constructor", "prototype"].includes(n) || (Array.isArray(e[n]) && Array.isArray(i[n]) ? e[n] = m.options.mergeArrays ? Array.from(new Set(e[n].concat(i[n]))) : i[n] : N(e[n]) && N(i[n]) ? e[n] = m(e[n], i[n]) : e[n] = i[n]);
}), e;
}, {});
}
const z = {
mergeArrays: !0
};
m.options = z;
m.withOptions = (t, ...e) => {
m.options = {
mergeArrays: !0,
...t
};
const i = m(...e);
return m.options = z, i;
};
function K() {
return {
centering: {
enabled: !0,
strength: 0.1
},
charge: {
enabled: !0,
strength: -1
},
collision: {
enabled: !0,
strength: 1,
radiusMultiplier: 2
},
link: {
enabled: !0,
strength: 1,
length: 128
}
};
}
function J() {
return {
includeUnlinked: !0,
linkFilter: () => !0,
nodeTypeFilter: void 0,
showLinkLabels: !0,
showNodeLabels: !0
};
}
function O(t) {
t.preventDefault(), t.stopPropagation();
}
function v(t) {
return typeof t == "number";
}
function g(t, e) {
return v(t.nodeRadius) ? t.nodeRadius : t.nodeRadius(e);
}
function Q(t) {
return `${t.source.id}-${t.target.id}`;
}
function A(t) {
return `link-arrow-${t}`.replace(/[()]/g, "~");
}
function ee(t) {
return `url(#${A(t.color)})`;
}
function te(t) {
return {
size: t,
padding: (e, i) => g(i, e) + 2 * t,
ref: [t / 2, t / 2],
path: [
[0, 0],
[0, t],
[t, t / 2]
],
viewBox: [0, 0, t, t].join(",")
};
}
const ie = {
/**
* Create an arrow marker configuration.
* @param size - The size of the arrow
*/
Arrow: (t) => te(t)
}, ne = (t, e, i) => [e / 2, i / 2], C = (t, e, i) => [$(0, e), $(0, i)];
function $(t, e) {
return Math.random() * (e - t) + t;
}
function re(t) {
const e = Object.fromEntries(
t.nodes.map((i) => [i.id, [i.x, i.y]])
);
return (i, n, r) => {
const [o, a] = e[i.id] ?? [];
return !o || !a ? C(i, n, r) : [o, a];
};
}
const se = {
/**
* Initializes node positions to a graph's center.
*/
Centered: ne,
/**
* Randomly initializes node positions within the visible area.
*/
Randomized: C,
/**
* Initializes node positions based on other graph.
*/
Stable: re
};
function oe() {
return {
autoResize: !1,
callbacks: {},
hooks: {},
initial: J(),
nodeRadius: 16,
marker: ie.Arrow(4),
modifiers: {},
positionInitializer: se.Centered,
simulation: {
alphas: P(),
forces: K()
},
zoom: {
initial: 1,
min: 0.1,
max: 2
}
};
}
function He(t = {}) {
return m.withOptions(
{ mergeArrays: !1 },
oe(),
t
);
}
function ae({
applyZoom: t,
container: e,
onDoubleClick: i,
onPointerMoved: n,
onPointerUp: r,
offset: [o, a],
scale: s,
zoom: l
}) {
const c = e.classed("graph", !0).append("svg").attr("height", "100%").attr("width", "100%").call(l).on("contextmenu", (u) => O(u)).on("dblclick", (u) => i == null ? void 0 : i(u)).on("dblclick.zoom", null).on("pointermove", (u) => n == null ? void 0 : n(u)).on("pointerup", (u) => r == null ? void 0 : r(u)).style("cursor", "grab");
return t && c.call(
l.transform,
E.translate(o, a).scale(s)
), c.append("g");
}
function le({
canvas: t,
scale: e,
xOffset: i,
yOffset: n
}) {
t == null || t.attr("transform", `translate(${i},${n})scale(${e})`);
}
function ce({
config: t,
onDragStart: e,
onDragEnd: i
}) {
var r, o;
const n = Z().filter((a) => a.type === "mousedown" ? a.button === 0 : a.type === "touchstart" ? a.touches.length === 1 : !1).on("start", (a, s) => {
a.active === 0 && e(a, s), w(a.sourceEvent.target).classed("grabbed", !0), s.fx = s.x, s.fy = s.y;
}).on("drag", (a, s) => {
s.fx = a.x, s.fy = a.y;
}).on("end", (a, s) => {
a.active === 0 && i(a, s), w(a.sourceEvent.target).classed("grabbed", !1), s.fx = void 0, s.fy = void 0;
});
return (o = (r = t.modifiers).drag) == null || o.call(r, n), n;
}
function de({
graph: t,
filter: e,
focusedNode: i,
includeUnlinked: n,
linkFilter: r
}) {
const o = t.links.filter(
(l) => e.includes(l.source.type) && e.includes(l.target.type) && r(l)
), a = (l) => o.find(
(c) => c.source.id === l.id || c.target.id === l.id
) !== void 0, s = t.nodes.filter(
(l) => e.includes(l.type) && (n || a(l))
);
return i === void 0 || !e.includes(i.type) ? {
nodes: s,
links: o
} : ue({ nodes: s, links: o }, i);
}
function ue(t, e) {
const i = [
...he(t, e),
...fe(t, e)
], n = i.flatMap((r) => [r.source, r.target]);
return {
nodes: [.../* @__PURE__ */ new Set([...n, e])],
links: [...new Set(i)]
};
}
function he(t, e) {
return G(
t,
e,
(i, n) => i.target.id === n.id
);
}
function fe(t, e) {
return G(
t,
e,
(i, n) => i.source.id === n.id
);
}
function G(t, e, i) {
const n = new Set(t.links), r = /* @__PURE__ */ new Set([e]), o = [];
for (; n.size > 0; ) {
const a = [...n].filter(
(s) => [...r].some((l) => i(s, l))
);
if (a.length === 0)
return o;
a.forEach((s) => {
r.add(s.source), r.add(s.target), o.push(s), n.delete(s);
});
}
return o;
}
function L(t) {
return t.x ?? 0;
}
function _(t) {
return t.y ?? 0;
}
function T({ source: t, target: e }) {
const i = new k(L(t), _(t)), n = new k(L(e), _(e)), r = n.subtract(i), o = r.length(), a = r.normalize(), s = a.multiply(-1);
return {
s: i,
t: n,
dist: o,
norm: a,
endNorm: s
};
}
function B({ center: t, node: e }) {
const i = new k(L(e), _(e));
let n = t;
return i.x === n.x && i.y === n.y && (n = n.add(new k(0, 1))), {
n: i,
c: n
};
}
function D({ config: t, source: e, target: i }) {
const { s: n, t: r, norm: o } = T({ config: t, source: e, target: i }), a = n.add(o.multiply(g(t, e) - 1)), s = r.subtract(o.multiply(t.marker.padding(i, t)));
return {
start: a,
end: s
};
}
function pe(t) {
const { start: e, end: i } = D(t);
return `M${e.x},${e.y}
L${i.x},${i.y}`;
}
function me(t) {
const { start: e, end: i } = D(t), n = i.subtract(e).multiply(0.5), r = e.add(n);
return `translate(${r.x - 8},${r.y - 4})`;
}
function ge({ config: t, source: e, target: i }) {
const { s: n, t: r, dist: o, norm: a, endNorm: s } = T({
config: t,
source: e,
target: i
}), l = 10, c = a.rotateByDegrees(-10).multiply(g(t, e) - 1).add(n), u = s.rotateByDegrees(l).multiply(g(t, i)).add(r).add(s.rotateByDegrees(l).multiply(2 * t.marker.size)), p = 1.2 * o;
return `M${c.x},${c.y}
A${p},${p},0,0,1,${u.x},${u.y}`;
}
function ye({ center: t, config: e, node: i }) {
const { n, c: r } = B({ center: t, config: e, node: i }), o = g(e, i), a = n.subtract(r), s = a.multiply(1 / a.length()), c = s.rotateByDegrees(40).multiply(o - 1).add(n), u = s.rotateByDegrees(-40).multiply(o).add(n).add(s.rotateByDegrees(-40).multiply(2 * e.marker.size));
return `M${c.x},${c.y}
A${o},${o},0,1,0,${u.x},${u.y}`;
}
function ke({ config: t, source: e, target: i }) {
const { t: n, dist: r, endNorm: o } = T({ config: t, source: e, target: i }), s = o.rotateByDegrees(10).multiply(0.5 * r).add(n);
return `translate(${s.x},${s.y})`;
}
function be({ center: t, config: e, node: i }) {
const { n, c: r } = B({ center: t, config: e, node: i }), o = n.subtract(r), a = o.multiply(1 / o.length()).multiply(3 * g(e, i) + 8).add(n);
return `translate(${a.x},${a.y})`;
}
const y = {
line: {
labelTransform: me,
path: pe
},
arc: {
labelTransform: ke,
path: ge
},
reflexive: {
labelTransform: be,
path: ye
}
};
function we(t) {
return t.append("g").classed("links", !0).selectAll("path");
}
function xe({
config: t,
graph: e,
selection: i,
showLabels: n
}) {
const r = i == null ? void 0 : i.data(e.links, (o) => Q(o)).join((o) => {
var c, u, p, b;
const a = o.append("g"), s = a.append("path").classed("link", !0).style("marker-end", (f) => ee(f)).style("stroke", (f) => f.color);
(u = (c = t.modifiers).link) == null || u.call(c, s);
const l = a.append("text").classed("link__label", !0).style("fill", (f) => f.label ? f.label.color : null).style("font-size", (f) => f.label ? f.label.fontSize : null).text((f) => f.label ? f.label.text : null);
return (b = (p = t.modifiers).linkLabel) == null || b.call(p, l), a;
});
return r == null || r.select(".link__label").attr("opacity", (o) => o.label && n ? 1 : 0), r;
}
function Le(t) {
_e(t), Te(t);
}
function _e({
center: t,
config: e,
graph: i,
selection: n
}) {
n == null || n.selectAll("path").attr("d", (r) => r.source.x === void 0 || r.source.y === void 0 || r.target.x === void 0 || r.target.y === void 0 ? "" : r.source.id === r.target.id ? y.reflexive.path({
config: e,
node: r.source,
center: t
}) : R(i, r.source, r.target) ? y.arc.path({ config: e, source: r.source, target: r.target }) : y.line.path({ config: e, source: r.source, target: r.target }));
}
function Te({
config: t,
center: e,
graph: i,
selection: n
}) {
n == null || n.select(".link__label").attr("transform", (r) => r.source.x === void 0 || r.source.y === void 0 || r.target.x === void 0 || r.target.y === void 0 ? "translate(0, 0)" : r.source.id === r.target.id ? y.reflexive.labelTransform({
config: t,
node: r.source,
center: e
}) : R(i, r.source, r.target) ? y.arc.labelTransform({
config: t,
source: r.source,
target: r.target
}) : y.line.labelTransform({
config: t,
source: r.source,
target: r.target
}));
}
function R(t, e, i) {
return e.id !== i.id && t.links.some(
(n) => n.target.id === e.id && n.source.id === i.id
) && t.links.some(
(n) => n.target.id === i.id && n.source.id === e.id
);
}
function Se(t) {
return t.append("defs").selectAll("marker");
}
function Fe({
config: t,
graph: e,
selection: i
}) {
return i == null ? void 0 : i.data(Ne(e), (n) => n).join((n) => {
const r = n.append("marker").attr("id", (o) => A(o)).attr("markerHeight", 4 * t.marker.size).attr("markerWidth", 4 * t.marker.size).attr("markerUnits", "userSpaceOnUse").attr("orient", "auto").attr("refX", t.marker.ref[0]).attr("refY", t.marker.ref[1]).attr("viewBox", t.marker.viewBox).style("fill", (o) => o);
return r.append("path").attr("d", $e(t.marker.path)), r;
});
}
function Ne(t) {
return [...new Set(t.links.map((e) => e.color))];
}
function $e(t) {
const [e, ...i] = t;
if (!e)
return "M0,0";
const [n, r] = e;
return i.reduce(
(o, [a, s]) => `${o}L${a},${s}`,
`M${n},${r}`
);
}
function ze(t) {
return t.append("g").classed("nodes", !0).selectAll("circle");
}
function Oe({
config: t,
drag: e,
graph: i,
onNodeContext: n,
onNodeSelected: r,
selection: o,
showLabels: a
}) {
const s = o == null ? void 0 : o.data(i.nodes, (l) => l.id).join((l) => {
var b, f, S, F;
const c = l.append("g");
e !== void 0 && c.call(e);
const u = c.append("circle").classed("node", !0).attr("r", (h) => g(t, h)).on("contextmenu", (h, x) => {
O(h), n(x);
}).on("pointerdown", (h, x) => Ae(h, x, r ?? n)).style("fill", (h) => h.color);
(f = (b = t.modifiers).node) == null || f.call(b, u);
const p = c.append("text").classed("node__label", !0).attr("dy", "0.33em").style("fill", (h) => h.label ? h.label.color : null).style("font-size", (h) => h.label ? h.label.fontSize : null).style("stroke", "none").text((h) => h.label ? h.label.text : null);
return (F = (S = t.modifiers).nodeLabel) == null || F.call(S, p), c;
});
return s == null || s.select(".node").classed("focused", (l) => l.isFocused), s == null || s.select(".node__label").attr("opacity", a ? 1 : 0), s;
}
const ve = 500;
function Ae(t, e, i) {
if (t.button !== void 0 && t.button !== 0)
return;
const n = e.lastInteractionTimestamp, r = Date.now();
if (n === void 0 || r - n > ve) {
e.lastInteractionTimestamp = r;
return;
}
e.lastInteractionTimestamp = void 0, i(e);
}
function Ce(t) {
t == null || t.attr("transform", (e) => `translate(${e.x ?? 0},${e.y ?? 0})`);
}
function Ge({
center: t,
config: e,
graph: i,
onTick: n
}) {
var c, u;
const r = W(i.nodes), o = e.simulation.forces.centering;
if (o && o.enabled) {
const p = o.strength;
r.force("x", H(() => t().x).strength(p)).force("y", V(() => t().y).strength(p));
}
const a = e.simulation.forces.charge;
a && a.enabled && r.force(
"charge",
X().strength(a.strength)
);
const s = e.simulation.forces.collision;
s && s.enabled && r.force(
"collision",
Y().radius(
(p) => s.radiusMultiplier * g(e, p)
)
);
const l = e.simulation.forces.link;
return l && l.enabled && r.force(
"link",
q(i.links).id((p) => p.id).distance(e.simulation.forces.link.length).strength(l.strength)
), r.on("tick", () => n()), (u = (c = e.modifiers).simulation) == null || u.call(c, r), r;
}
function Be({
canvasContainer: t,
config: e,
min: i,
max: n,
onZoom: r
}) {
var a, s;
const o = U().scaleExtent([i, n]).filter((l) => {
var c;
return l.button === 0 || ((c = l.touches) == null ? void 0 : c.length) >= 2;
}).on("start", () => t().classed("grabbed", !0)).on("zoom", (l) => r(l)).on("end", () => t().classed("grabbed", !1));
return (s = (a = e.modifiers).zoom) == null || s.call(a, o), o;
}
class Ve {
/**
* Create a new controller and initialize the view.
* @param container - The container the graph will be placed in.
* @param graph - The graph of the controller.
* @param config - The config of the controller.
*/
constructor(e, i, n) {
/**
* Array of all node types included in the controller's graph.
*/
d(this, "nodeTypes");
d(this, "_nodeTypeFilter");
d(this, "_includeUnlinked", !0);
d(this, "_linkFilter", () => !0);
d(this, "_showLinkLabels", !0);
d(this, "_showNodeLabels", !0);
d(this, "filteredGraph");
d(this, "width", 0);
d(this, "height", 0);
d(this, "simulation");
d(this, "canvas");
d(this, "linkSelection");
d(this, "nodeSelection");
d(this, "markerSelection");
d(this, "zoom");
d(this, "drag");
d(this, "xOffset", 0);
d(this, "yOffset", 0);
d(this, "scale");
d(this, "focusedNode");
d(this, "resizeObserver");
if (this.container = e, this.graph = i, this.config = n, this.scale = n.zoom.initial, this.resetView(), this.graph.nodes.forEach((r) => {
const [o, a] = n.positionInitializer(
r,
this.effectiveWidth,
this.effectiveHeight
);
r.x = r.x ?? o, r.y = r.y ?? a;
}), this.nodeTypes = [...new Set(i.nodes.map((r) => r.type))], this._nodeTypeFilter = [...this.nodeTypes], n.initial) {
const {
includeUnlinked: r,
nodeTypeFilter: o,
linkFilter: a,
showLinkLabels: s,
showNodeLabels: l
} = n.initial;
this._includeUnlinked = r ?? this._includeUnlinked, this._showLinkLabels = s ?? this._showLinkLabels, this._showNodeLabels = l ?? this._showNodeLabels, this._nodeTypeFilter = o ?? this._nodeTypeFilter, this._linkFilter = a ?? this._linkFilter;
}
this.filterGraph(void 0), this.initGraph(), this.restart(n.simulation.alphas.initialize), n.autoResize && (this.resizeObserver = new ResizeObserver(M(() => this.resize())), this.resizeObserver.observe(this.container));
}
/**
* Get the current node type filter.
* Only nodes whose type is included will be shown.
*/
get nodeTypeFilter() {
return this._nodeTypeFilter;
}
/**
* Get whether nodes without incoming or outgoing links will be shown or not.
*/
get includeUnlinked() {
return this._includeUnlinked;
}
/**
* Set whether nodes without incoming or outgoing links will be shown or not.
* @param value - The value.
*/
set includeUnlinked(e) {
this._includeUnlinked = e, this.filterGraph(this.focusedNode);
const { include: i, exclude: n } = this.config.simulation.alphas.filter.unlinked, r = e ? i : n;
this.restart(r);
}
/**
* Set a new link filter and update the controller's state.
* @param value - The new link filter.
*/
set linkFilter(e) {
this._linkFilter = e, this.filterGraph(this.focusedNode), this.restart(this.config.simulation.alphas.filter.link);
}
/**
* Get the current link filter.
* @returns - The current link filter.
*/
get linkFilter() {
return this._linkFilter;
}
/**
* Get whether node labels are shown or not.
*/
get showNodeLabels() {
return this._showNodeLabels;
}
/**
* Set whether node labels will be shown or not.
* @param value - The value.
*/
set showNodeLabels(e) {
this._showNodeLabels = e;
const { hide: i, show: n } = this.config.simulation.alphas.labels.nodes, r = e ? n : i;
this.restart(r);
}
/**
* Get whether link labels are shown or not.
*/
get showLinkLabels() {
return this._showLinkLabels;
}
/**
* Set whether link labels will be shown or not.
* @param value - The value.
*/
set showLinkLabels(e) {
this._showLinkLabels = e;
const { hide: i, show: n } = this.config.simulation.alphas.labels.links, r = e ? n : i;
this.restart(r);
}
get effectiveWidth() {
return this.width / this.scale;
}
get effectiveHeight() {
return this.height / this.scale;
}
get effectiveCenter() {
return k.of([this.width, this.height]).divide(2).subtract(k.of([this.xOffset, this.yOffset])).divide(this.scale);
}
/**
* Resize the graph to fit its container.
*/
resize() {
const e = this.width, i = this.height, n = this.container.getBoundingClientRect().width, r = this.container.getBoundingClientRect().height, o = e.toFixed() !== n.toFixed(), a = i.toFixed() !== r.toFixed();
if (!o && !a)
return;
this.width = this.container.getBoundingClientRect().width, this.height = this.container.getBoundingClientRect().height;
const s = this.config.simulation.alphas.resize;
this.restart(
v(s) ? s : s({ oldWidth: e, oldHeight: i, newWidth: n, newHeight: r })
);
}
/**
* Restart the controller.
* @param alpha - The alpha value of the controller's simulation after the restart.
*/
restart(e) {
var i;
this.markerSelection = Fe({
config: this.config,
graph: this.filteredGraph,
selection: this.markerSelection
}), this.linkSelection = xe({
config: this.config,
graph: this.filteredGraph,
selection: this.linkSelection,
showLabels: this._showLinkLabels
}), this.nodeSelection = Oe({
config: this.config,
drag: this.drag,
graph: this.filteredGraph,
onNodeContext: (n) => this.toggleNodeFocus(n),
onNodeSelected: this.config.callbacks.nodeClicked,
selection: this.nodeSelection,
showLabels: this._showNodeLabels
}), (i = this.simulation) == null || i.stop(), this.simulation = Ge({
center: () => this.effectiveCenter,
config: this.config,
graph: this.filteredGraph,
onTick: () => this.onTick()
}).alpha(e).restart();
}
/**
* Update the node type filter by either including or removing the specified type from the filter.
* @param include - Whether the type will be included or removed from the filter.
* @param nodeType - The type to be added or removed from the filter.
*/
filterNodesByType(e, i) {
e ? this._nodeTypeFilter.push(i) : this._nodeTypeFilter = this._nodeTypeFilter.filter(
(n) => n !== i
), this.filterGraph(this.focusedNode), this.restart(this.config.simulation.alphas.filter.type);
}
/**
* Shut down the controller's simulation and (optional) automatic resizing.
*/
shutdown() {
var e, i;
this.focusedNode !== void 0 && (this.focusedNode.isFocused = !1, this.focusedNode = void 0), (e = this.resizeObserver) == null || e.unobserve(this.container), (i = this.simulation) == null || i.stop();
}
initGraph() {
this.zoom = Be({
config: this.config,
canvasContainer: () => w(this.container).select("svg"),
min: this.config.zoom.min,
max: this.config.zoom.max,
onZoom: (e) => this.onZoom(e)
}), this.canvas = ae({
applyZoom: this.scale !== 1,
container: w(this.container),
offset: [this.xOffset, this.yOffset],
scale: this.scale,
zoom: this.zoom
}), this.applyZoom(), this.linkSelection = we(this.canvas), this.nodeSelection = ze(this.canvas), this.markerSelection = Se(this.canvas), this.drag = ce({
config: this.config,
onDragStart: () => {
var e;
return (e = this.simulation) == null ? void 0 : e.alphaTarget(this.config.simulation.alphas.drag.start).restart();
},
onDragEnd: () => {
var e;
return (e = this.simulation) == null ? void 0 : e.alphaTarget(this.config.simulation.alphas.drag.end).restart();
}
});
}
onTick() {
Ce(this.nodeSelection), Le({
config: this.config,
center: this.effectiveCenter,
graph: this.filteredGraph,
selection: this.linkSelection
});
}
resetView() {
var e;
(e = this.simulation) == null || e.stop(), w(this.container).selectChildren().remove(), this.zoom = void 0, this.canvas = void 0, this.linkSelection = void 0, this.nodeSelection = void 0, this.markerSelection = void 0, this.simulation = void 0, this.width = this.container.getBoundingClientRect().width, this.height = this.container.getBoundingClientRect().height;
}
onZoom(e) {
var i, n, r;
this.xOffset = e.transform.x, this.yOffset = e.transform.y, this.scale = e.transform.k, this.applyZoom(), (n = (i = this.config.hooks).afterZoom) == null || n.call(i, this.scale, this.xOffset, this.yOffset), (r = this.simulation) == null || r.restart();
}
applyZoom() {
le({
canvas: this.canvas,
scale: this.scale,
xOffset: this.xOffset,
yOffset: this.yOffset
});
}
toggleNodeFocus(e) {
e.isFocused ? (this.filterGraph(void 0), this.restart(this.config.simulation.alphas.focus.release(e))) : this.focusNode(e);
}
focusNode(e) {
this.filterGraph(e), this.restart(this.config.simulation.alphas.focus.acquire(e));
}
filterGraph(e) {
this.focusedNode !== void 0 && (this.focusedNode.isFocused = !1, this.focusedNode = void 0), e !== void 0 && this._nodeTypeFilter.includes(e.type) && (e.isFocused = !0, this.focusedNode = e), this.filteredGraph = de({
graph: this.graph,
filter: this._nodeTypeFilter,
focusedNode: this.focusedNode,
includeUnlinked: this._includeUnlinked,
linkFilter: this._linkFilter
});
}
}
function Xe({ nodes: t, links: e }) {
return {
nodes: t ?? [],
links: e ?? []
};
}
function Ye(t) {
return {
...t
};
}
function De(t) {
return {
...t,
isFocused: !1,
lastInteractionTimestamp: void 0
};
}
const Re = {
color: "lightgray",
label: {
color: "black",
fontSize: "1rem",
text: ""
},
isFocused: !1
};
function qe(t) {
return De({
...Re,
...t
});
}
export {
Ve as GraphController,
ie as Markers,
se as PositionInitializers,
P as createDefaultAlphaConfig,
K as createDefaultForceConfig,
J as createDefaultInitialGraphSettings,
Xe as defineGraph,
He as defineGraphConfig,
Ye as defineLink,
De as defineNode,
qe as defineNodeWithDefaults
};