UNPKG

d3-graph-controller

Version:

A TypeScript library for visualizing and simulating directed, interactive graphs.

836 lines (835 loc) 24.9 kB
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 };