@logixode/force-graph-lib 0.4.1 → 0.5.1

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.
@@ -1,6 +1,6 @@
1
- import f from "force-graph";
2
- import * as c from "d3";
3
- class m {
1
+ import c from "force-graph";
2
+ import "d3";
3
+ class C {
4
4
  container;
5
5
  graph;
6
6
  data = { nodes: [], links: [] };
@@ -10,15 +10,15 @@ class m {
10
10
  worker = null;
11
11
  groupBounds = /* @__PURE__ */ new Map();
12
12
  isFirstRender = !0;
13
- constructor(t, i = { nodes: [], links: [] }, o = {}) {
14
- this.container = t, this.data = i, this.graph = new f(this.container), i.nodes.forEach((r) => {
13
+ constructor(t, o = { nodes: [], links: [] }, i = {}) {
14
+ this.container = t, this.data = o, this.graph = new c(this.container), o.nodes.forEach((r) => {
15
15
  this.nodesMap.set(r.id.toString(), r);
16
16
  });
17
17
  const e = {
18
18
  labelThreshold: 1.5,
19
19
  // Show labels when they're not bigger than the node
20
20
  showGroups: !1,
21
- ...o
21
+ ...i
22
22
  }, s = e.showGroups ? {
23
23
  groupBy: "topic",
24
24
  groupBorderColor: "#666",
@@ -49,29 +49,29 @@ class m {
49
49
  focusPosition(t = {}) {
50
50
  if (Object.values(t).length) {
51
51
  if (t.id) {
52
- const { x: i, y: o } = this.nodesMap.get(t.id) ?? { x: 0, y: 0 };
53
- i && o && (t = { x: i, y: o });
52
+ const { x: o, y: i } = this.nodesMap.get(t.id) ?? { x: 0, y: 0 };
53
+ o && i && (t = { x: o, y: i });
54
54
  }
55
55
  t && (this.graph.centerAt(t.x, t.y, 1e3), this.graph.zoom(8, 2e3));
56
56
  }
57
57
  }
58
58
  render() {
59
- this.graph.width(this.options.width ?? 800).height(this.options.height ?? 400).cooldownTime(this.getCooldownTime()).d3Force("charge", c.forceManyBody().strength(this.options.nodeGap ?? -50)), this.options.cooldownTicks !== void 0 && this.graph.cooldownTicks(this.options.cooldownTicks);
59
+ this.graph.width(this.options.width ?? 800).height(this.options.height ?? 400), this.options.cooldownTicks !== void 0 && this.graph.cooldownTicks(this.options.cooldownTicks);
60
60
  }
61
- force(t, i) {
62
- return this.graph.d3Force(t, i);
61
+ force(t, o) {
62
+ return this.graph.d3Force(t, o);
63
63
  }
64
64
  applyOptions() {
65
65
  this.options.keepDragPosition && this.graph.onNodeDragEnd((t) => {
66
66
  t.fx = t.x, t.fy = t.y;
67
- }), this.options.pointerInteraction || this.graph.enablePointerInteraction(!1), this.graph.nodeCanvasObject((t, i, o) => {
68
- t === this.data.nodes[0] && this.renderGroups(i, o);
69
- const e = this.getNodeSize(t) * 2, s = typeof this.options.nodeBorderWidth == "function" ? this.options.nodeBorderWidth(t) : this.options.nodeBorderWidth;
70
- i.beginPath(), i.arc(t.x || 0, t.y || 0, e, 0, 2 * Math.PI), i.fillStyle = this.getNodeColor(t), i.fill(), s && s > 0 && (i.beginPath(), i.arc(t.x || 0, t.y || 0, e, 0, 2 * Math.PI), i.strokeStyle = this.getNodeBorderColor(t), i.lineWidth = s, i.stroke());
71
- const r = this.getNodeLabel(t);
72
- if (r && this.shouldShowLabel(t, o)) {
73
- const a = typeof this.options.nodeLabelColor == "function" ? this.options.nodeLabelColor(t) : this.options.nodeLabelColor ?? "#555", n = (this.options.labelFontSize || 14) / o;
74
- i.font = `${n}px Arial`, i.fillStyle = a, i.textAlign = "center", i.textBaseline = "middle", i.fillText(r, t.x || 0, (t.y || 0) + e + n / 2 + 2);
67
+ }), this.options.pointerInteraction || this.graph.enablePointerInteraction(!1), this.graph.nodeCanvasObject((t, o, i) => {
68
+ t === this.data.nodes[0] && this.renderGroups(o, i);
69
+ const e = this.getNodeSize(t) * 2, s = typeof this.options.nodeBorderWidth == "function" ? this.options.nodeBorderWidth(t) : this.options.nodeBorderWidth, r = this.resolveNodeShape(t), a = t.x || 0, h = t.y || 0;
70
+ this.drawNodeShape(o, a, h, e, r), o.fillStyle = this.getNodeColor(t), o.fill(), s && s > 0 && (this.drawNodeShape(o, a, h, e, r), o.strokeStyle = this.getNodeBorderColor(t), o.lineWidth = s, o.stroke());
71
+ const n = this.getNodeLabel(t);
72
+ if (n && this.shouldShowLabel(t, i)) {
73
+ const p = typeof this.options.nodeLabelColor == "function" ? this.options.nodeLabelColor(t) : this.options.nodeLabelColor ?? "#555", l = (this.options.labelFontSize || 14) / i;
74
+ o.font = `${l}px Arial`, o.fillStyle = p, o.textAlign = "center", o.textBaseline = "middle", o.fillText(n, t.x || 0, (t.y || 0) + e + l / 2 + 2);
75
75
  }
76
76
  }), this.applyLinkOptions();
77
77
  }
@@ -85,13 +85,13 @@ class m {
85
85
  * Check if label should be shown based on threshold logic
86
86
  * The threshold determines when labels become too big relative to nodes
87
87
  */
88
- shouldShowLabel(t, i) {
89
- const o = this.getNodeSize(t) * 2, s = (this.options.labelFontSize || 14) / i, r = this.options.labelThreshold || 1.5;
90
- return s <= o * r;
88
+ shouldShowLabel(t, o) {
89
+ const i = this.getNodeSize(t) * 2, s = (this.options.labelFontSize || 14) / o, r = this.options.labelThreshold || 1.5;
90
+ return s <= i * r;
91
91
  }
92
92
  updateData(t) {
93
- const i = new Set(this.data.nodes.map((r) => r.id.toString())), o = t.nodes.filter((r) => !i.has(r.id.toString()));
94
- o.forEach((r) => {
93
+ const o = new Set(this.data.nodes.map((r) => r.id.toString())), i = t.nodes.filter((r) => !o.has(r.id.toString()));
94
+ i.forEach((r) => {
95
95
  this.nodesMap.set(r.id.toString(), r);
96
96
  });
97
97
  const e = new Set(
@@ -100,20 +100,61 @@ class m {
100
100
  (r) => !e.has(this.createLinkKey(r.source, r.target))
101
101
  );
102
102
  this.data = {
103
- nodes: [...this.data.nodes, ...o],
103
+ nodes: [...this.data.nodes, ...i],
104
104
  links: [...this.data.links, ...s]
105
105
  }, this.refreshGraph();
106
106
  }
107
107
  getNodeColor(t) {
108
108
  if (typeof this.options.nodeColor == "function") {
109
- const i = this.options.nodeColor(t);
110
- if (i) return i;
109
+ const o = this.options.nodeColor(t);
110
+ if (o) return o;
111
111
  }
112
112
  return t?.color ?? "";
113
113
  }
114
114
  getNodeBorderColor(t) {
115
115
  return typeof this.options.nodeBorderColor == "function" ? this.options.nodeBorderColor(t) : this.options.nodeBorderColor || "#333";
116
116
  }
117
+ resolveNodeShape(t) {
118
+ return t.shape ? t.shape : typeof this.options.nodeShape == "function" ? this.options.nodeShape(t) : this.options.nodeShape ?? "circle";
119
+ }
120
+ drawNodeShape(t, o, i, e, s) {
121
+ switch (t.beginPath(), s) {
122
+ case "square": {
123
+ t.rect(o - e, i - e, e * 2, e * 2);
124
+ break;
125
+ }
126
+ case "triangle": {
127
+ const r = e * Math.sqrt(3);
128
+ t.moveTo(o, i - e), t.lineTo(o + r / 2, i + e / 2), t.lineTo(o - r / 2, i + e / 2), t.closePath();
129
+ break;
130
+ }
131
+ case "diamond": {
132
+ t.moveTo(o, i - e), t.lineTo(o + e, i), t.lineTo(o, i + e), t.lineTo(o - e, i), t.closePath();
133
+ break;
134
+ }
135
+ case "star": {
136
+ const r = e, a = e * 0.4, h = 5;
137
+ for (let n = 0; n < h * 2; n++) {
138
+ const p = n % 2 === 0 ? r : a, d = Math.PI / h * n - Math.PI / 2, l = o + p * Math.cos(d), u = i + p * Math.sin(d);
139
+ n === 0 ? t.moveTo(l, u) : t.lineTo(l, u);
140
+ }
141
+ t.closePath();
142
+ break;
143
+ }
144
+ case "hexagon": {
145
+ for (let r = 0; r < 6; r++) {
146
+ const a = Math.PI / 3 * r - Math.PI / 6, h = o + e * Math.cos(a), n = i + e * Math.sin(a);
147
+ r === 0 ? t.moveTo(h, n) : t.lineTo(h, n);
148
+ }
149
+ t.closePath();
150
+ break;
151
+ }
152
+ case "circle":
153
+ default:
154
+ t.arc(o, i, e, 0, 2 * Math.PI);
155
+ break;
156
+ }
157
+ }
117
158
  applyLinkOptions() {
118
159
  this.graph.linkColor((t) => typeof this.options.linkColor == "function" ? this.options.linkColor(t) : this.options.linkColor ? this.options.linkColor : t.color ?? ""), this.options.linkWidth !== void 0 && this.graph.linkWidth(
119
160
  (t) => this.getLinkProperty(this.options.linkWidth, t) ?? 1
@@ -130,8 +171,8 @@ class m {
130
171
  getLinkCurvature(t) {
131
172
  return typeof this.options.linkCurvature == "function" ? this.options.linkCurvature(t) : typeof this.options.linkCurvature == "string" ? t[this.options.linkCurvature] || 0 : typeof this.options.linkCurvature == "number" ? this.options.linkCurvature : t.curvature || 0;
132
173
  }
133
- getLinkProperty(t, i) {
134
- return typeof t == "function" ? t(i) : t;
174
+ getLinkProperty(t, o) {
175
+ return typeof t == "function" ? t(o) : t;
135
176
  }
136
177
  /**
137
178
  * Calculate group boundaries based on node positions
@@ -139,24 +180,24 @@ class m {
139
180
  calculateGroupBounds() {
140
181
  if (!this.options.showGroups) return;
141
182
  this.groupBounds.clear();
142
- const t = this.options.groupPadding || 20, i = /* @__PURE__ */ new Map();
143
- this.data.nodes.forEach((o) => {
144
- const e = this.getNodeGroupId(o);
145
- e && (i.has(e) || i.set(e, []), i.get(e).push(o));
146
- }), i.forEach((o, e) => {
147
- if (o.length === 0) return;
148
- let s = 1 / 0, r = 1 / 0, a = -1 / 0, p = -1 / 0;
149
- o.forEach((n) => {
183
+ const t = this.options.groupPadding || 20, o = /* @__PURE__ */ new Map();
184
+ this.data.nodes.forEach((i) => {
185
+ const e = this.getNodeGroupId(i);
186
+ e && (o.has(e) || o.set(e, []), o.get(e).push(i));
187
+ }), o.forEach((i, e) => {
188
+ if (i.length === 0) return;
189
+ let s = 1 / 0, r = 1 / 0, a = -1 / 0, h = -1 / 0;
190
+ i.forEach((n) => {
150
191
  if (n.x !== void 0 && n.y !== void 0) {
151
- const h = this.getNodeSize(n);
152
- s = Math.min(s, n.x - h), r = Math.min(r, n.y - h), a = Math.max(a, n.x + h), p = Math.max(p, n.y + h);
192
+ const p = this.getNodeSize(n);
193
+ s = Math.min(s, n.x - p), r = Math.min(r, n.y - p), a = Math.max(a, n.x + p), h = Math.max(h, n.y + p);
153
194
  }
154
195
  }), s !== 1 / 0 && this.groupBounds.set(e, {
155
196
  minX: s - t,
156
197
  minY: r - t,
157
198
  maxX: a + t,
158
- maxY: p + t,
159
- nodes: o
199
+ maxY: h + t,
200
+ nodes: i
160
201
  });
161
202
  });
162
203
  }
@@ -174,21 +215,21 @@ class m {
174
215
  /**
175
216
  * Render group borders and labels
176
217
  */
177
- renderGroups(t, i) {
178
- this.options.showGroups && (this.calculateGroupBounds(), this.groupBounds.forEach((o, e) => {
218
+ renderGroups(t, o) {
219
+ this.options.showGroups && (this.calculateGroupBounds(), this.groupBounds.forEach((i, e) => {
179
220
  const s = this.getGroupBorderColor(e), r = this.options.groupBorderWidth || 2, a = this.options.groupBorderOpacity || 0.3;
180
- t.save(), t.globalAlpha = a, t.strokeStyle = s, t.lineWidth = r / i, t.setLineDash([10 / i, 5 / i]), t.strokeRect(o.minX, o.minY, o.maxX - o.minX, o.maxY - o.minY), t.restore();
181
- const p = this.options.groupLabelThreshold || 0.8;
182
- if (i <= p) {
183
- const n = this.getGroupLabelColor(e), h = (this.options.groupLabelSize || 16) / i;
184
- t.save(), t.font = `bold ${h}px Arial`, t.fillStyle = n, t.textAlign = "center", t.textBaseline = "middle";
185
- const l = (o.minX + o.maxX) / 2, d = o.minY - h / 2, u = t.measureText(e).width, g = h;
221
+ t.save(), t.globalAlpha = a, t.strokeStyle = s, t.lineWidth = r / o, t.setLineDash([10 / o, 5 / o]), t.strokeRect(i.minX, i.minY, i.maxX - i.minX, i.maxY - i.minY), t.restore();
222
+ const h = this.options.groupLabelThreshold || 0.8;
223
+ if (o <= h) {
224
+ const n = this.getGroupLabelColor(e), p = (this.options.groupLabelSize || 16) / o;
225
+ t.save(), t.font = `bold ${p}px Arial`, t.fillStyle = n, t.textAlign = "center", t.textBaseline = "middle";
226
+ const d = (i.minX + i.maxX) / 2, l = i.minY - p / 2, g = t.measureText(e).width, f = p;
186
227
  t.globalAlpha = 0.8, t.fillStyle = "rgba(255, 255, 255, 0.9)", t.fillRect(
187
- l - u / 2 - 4,
188
- d - g / 2 - 2,
189
- u + 8,
190
- g + 4
191
- ), t.globalAlpha = 1, t.fillStyle = n, t.fillText(e, l, d), t.restore();
228
+ d - g / 2 - 4,
229
+ l - f / 2 - 2,
230
+ g + 8,
231
+ f + 4
232
+ ), t.globalAlpha = 1, t.fillStyle = n, t.fillText(e, d, l), t.restore();
192
233
  }
193
234
  }));
194
235
  }
@@ -222,41 +263,41 @@ class m {
222
263
  getCooldownTime() {
223
264
  if (this.options.cooldownTime !== void 0)
224
265
  return this.options.cooldownTime;
225
- const i = this.data.nodes.length * (125 / 100);
226
- return Math.max(4e3, i);
266
+ const o = this.data.nodes.length * (125 / 100);
267
+ return Math.max(4e3, o);
227
268
  }
228
269
  getDataSize() {
229
- const { nodes: t, links: i } = this.graph.graphData();
270
+ const { nodes: t, links: o } = this.graph.graphData();
230
271
  return {
231
272
  nodes: t.length,
232
- links: i.length
273
+ links: o.length
233
274
  };
234
275
  }
235
276
  getAllNodeIds() {
236
277
  return Array.from(this.nodesMap.keys());
237
278
  }
238
- updateNode(t, i) {
239
- const o = this.nodesMap.get(t.toString());
240
- if (o) {
241
- Object.assign(o, i);
279
+ updateNode(t, o) {
280
+ const i = this.nodesMap.get(t.toString());
281
+ if (i) {
282
+ Object.assign(i, o);
242
283
  const e = this.data.nodes.findIndex((s) => s.id.toString() === t.toString());
243
- return e !== -1 && (this.data.nodes[e] = o), this.refreshGraph(), !0;
284
+ return e !== -1 && (this.data.nodes[e] = i), this.refreshGraph(), !0;
244
285
  }
245
286
  return !1;
246
287
  }
247
288
  removeNode(t) {
248
- const i = t.toString();
249
- return this.nodesMap.has(i) ? (this.nodesMap.delete(i), this.data.nodes = this.data.nodes.filter((o) => o.id.toString() !== i), this.data.links = this.data.links.filter((o) => {
250
- const e = typeof o.source == "object" ? o.source.id : o.source, s = typeof o.target == "object" ? o.target.id : o.target;
251
- return e?.toString() !== i && s?.toString() !== i;
252
- }), this.graph.cooldownTime(this.getCooldownTime()), this.refreshGraph(), !0) : !1;
289
+ const o = t.toString();
290
+ return this.nodesMap.has(o) ? (this.nodesMap.delete(o), this.data.nodes = this.data.nodes.filter((i) => i.id.toString() !== o), this.data.links = this.data.links.filter((i) => {
291
+ const e = typeof i.source == "object" ? i.source.id : i.source, s = typeof i.target == "object" ? i.target.id : i.target;
292
+ return e?.toString() !== o && s?.toString() !== o;
293
+ }), this.refreshGraph(), !0) : !1;
253
294
  }
254
295
  async addData(t) {
255
- t.nodes.forEach((i) => {
256
- this.nodesMap.has(i.id.toString()) || this.nodesMap.set(i.id.toString(), i);
257
- }), t.links.forEach((i) => {
258
- const o = this.createLinkKey(i.source, i.target);
259
- this.linkMap.has(o) ? console.log("link already exists", i) : this.linkMap.set(o, i);
296
+ t.nodes.forEach((o) => {
297
+ this.nodesMap.has(o.id.toString()) || this.nodesMap.set(o.id.toString(), o);
298
+ }), t.links.forEach((o) => {
299
+ const i = this.createLinkKey(o.source, o.target);
300
+ this.linkMap.has(i) ? console.log("link already exists", o) : this.linkMap.set(i, o);
260
301
  }), this.data = {
261
302
  nodes: Array.from(this.nodesMap.values()),
262
303
  links: Array.from(this.linkMap.values())
@@ -281,9 +322,7 @@ class m {
281
322
  this.initGraph();
282
323
  }
283
324
  reset() {
284
- this.graph.pauseAnimation();
285
- const t = this.graph.d3Force("simulation");
286
- t && t.stop(), this.data = { nodes: [], links: [] }, this.nodesMap.clear(), this.graph = new f(this.container), this.initGraph();
325
+ this.graph.pauseAnimation(), this.data = { nodes: [], links: [] }, this.nodesMap.clear(), this.graph = new c(this.container), this.initGraph();
287
326
  }
288
327
  getData() {
289
328
  return this.data;
@@ -294,20 +333,20 @@ class m {
294
333
  getLinksData() {
295
334
  return this.data.links;
296
335
  }
297
- createLinkKey(t, i) {
298
- const o = t === void 0 ? "undefined" : typeof t == "object" ? t.id : t, e = i === void 0 ? "undefined" : typeof i == "object" ? i.id : i;
299
- return `${o}-${e}`;
336
+ createLinkKey(t, o) {
337
+ const i = t === void 0 ? "undefined" : typeof t == "object" ? t.id : t, e = o === void 0 ? "undefined" : typeof o == "object" ? o.id : o;
338
+ return `${i}-${e}`;
300
339
  }
301
340
  /**
302
341
  * Set graph data (chainable method)
303
342
  * @param data - Graph data to set
304
343
  */
305
344
  graphData(t) {
306
- return this.nodesMap.clear(), this.linkMap.clear(), t.nodes.forEach((i) => {
307
- this.nodesMap.has(i.id.toString()) || this.nodesMap.set(i.id.toString(), i);
308
- }), t.links.forEach((i) => {
309
- const o = this.createLinkKey(i.source, i.target);
310
- this.linkMap.has(o) || this.linkMap.set(o, i);
345
+ return this.nodesMap.clear(), this.linkMap.clear(), t.nodes.forEach((o) => {
346
+ this.nodesMap.has(o.id.toString()) || this.nodesMap.set(o.id.toString(), o);
347
+ }), t.links.forEach((o) => {
348
+ const i = this.createLinkKey(o.source, o.target);
349
+ this.linkMap.has(i) || this.linkMap.set(i, o);
311
350
  }), this.data = {
312
351
  nodes: Array.from(this.nodesMap.values()),
313
352
  links: Array.from(this.linkMap.values())
@@ -326,8 +365,8 @@ class m {
326
365
  groupLabelSize: 16,
327
366
  groupLabelThreshold: 0.8,
328
367
  groupPadding: 20
329
- }).forEach(([o, e]) => {
330
- this.options[o] === void 0 && (this.options[o] = e);
368
+ }).forEach(([i, e]) => {
369
+ this.options[i] === void 0 && (this.options[i] = e);
331
370
  }), this.applyOptions(), this.refreshGraph(), this;
332
371
  }
333
372
  /**
@@ -347,16 +386,16 @@ class m {
347
386
  */
348
387
  getGroups() {
349
388
  const t = /* @__PURE__ */ new Set();
350
- return this.data.nodes.forEach((i) => {
351
- const o = this.getNodeGroupId(i);
352
- o && t.add(o);
389
+ return this.data.nodes.forEach((o) => {
390
+ const i = this.getNodeGroupId(o);
391
+ i && t.add(i);
353
392
  }), Array.from(t);
354
393
  }
355
394
  /**
356
395
  * Get nodes in a specific group
357
396
  */
358
397
  getNodesInGroup(t) {
359
- return this.data.nodes.filter((i) => this.getNodeGroupId(i) === t);
398
+ return this.data.nodes.filter((o) => this.getNodeGroupId(o) === t);
360
399
  }
361
400
  /**
362
401
  * Get current options
@@ -369,5 +408,5 @@ class m {
369
408
  }
370
409
  }
371
410
  export {
372
- m as ForceGraph
411
+ C as ForceGraph
373
412
  };
@@ -1 +1 @@
1
- (function(h,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("force-graph"),require("d3")):typeof define=="function"&&define.amd?define(["exports","force-graph","d3"],l):(h=typeof globalThis<"u"?globalThis:h||self,l(h.ForceGraphLib={},h.ForceGraph,h.d3))})(this,(function(h,l,k){"use strict";function b(d){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(d){for(const i in d)if(i!=="default"){const o=Object.getOwnPropertyDescriptor(d,i);Object.defineProperty(t,i,o.get?o:{enumerable:!0,get:()=>d[i]})}}return t.default=d,Object.freeze(t)}const m=b(k);class C{container;graph;data={nodes:[],links:[]};nodesMap=new Map;linkMap=new Map;options;worker=null;groupBounds=new Map;isFirstRender=!0;constructor(t,i={nodes:[],links:[]},o={}){this.container=t,this.data=i,this.graph=new l(this.container),i.nodes.forEach(r=>{this.nodesMap.set(r.id.toString(),r)});const e={labelThreshold:1.5,showGroups:!1,...o},s=e.showGroups?{groupBy:"topic",groupBorderColor:"#666",groupBorderWidth:2,groupBorderOpacity:.3,groupLabelColor:"#333",groupLabelSize:16,groupLabelThreshold:.8,groupPadding:20}:{};this.options={...e,...s},this.initGraph()}initGraph(){this.applyOptions(),this.render(),this.graphData(this.data),this.graph.onEngineStop(()=>{if(!this.data.nodes.length){this.isFirstRender=!0;return}this.isFirstRender?(this.options.onRenderComplete?this.options.onRenderComplete():this.graph.zoomToFit(400),this.isFirstRender=!1):this.options.onGraphUpdated&&this.options.onGraphUpdated()})}renderer(){return this.graph}focusPosition(t={}){if(Object.values(t).length){if(t.id){const{x:i,y:o}=this.nodesMap.get(t.id)??{x:0,y:0};i&&o&&(t={x:i,y:o})}t&&(this.graph.centerAt(t.x,t.y,1e3),this.graph.zoom(8,2e3))}}render(){this.graph.width(this.options.width??800).height(this.options.height??400).cooldownTime(this.getCooldownTime()).d3Force("charge",m.forceManyBody().strength(this.options.nodeGap??-50)),this.options.cooldownTicks!==void 0&&this.graph.cooldownTicks(this.options.cooldownTicks)}force(t,i){return this.graph.d3Force(t,i)}applyOptions(){this.options.keepDragPosition&&this.graph.onNodeDragEnd(t=>{t.fx=t.x,t.fy=t.y}),this.options.pointerInteraction||this.graph.enablePointerInteraction(!1),this.graph.nodeCanvasObject((t,i,o)=>{t===this.data.nodes[0]&&this.renderGroups(i,o);const e=this.getNodeSize(t)*2,s=typeof this.options.nodeBorderWidth=="function"?this.options.nodeBorderWidth(t):this.options.nodeBorderWidth;i.beginPath(),i.arc(t.x||0,t.y||0,e,0,2*Math.PI),i.fillStyle=this.getNodeColor(t),i.fill(),s&&s>0&&(i.beginPath(),i.arc(t.x||0,t.y||0,e,0,2*Math.PI),i.strokeStyle=this.getNodeBorderColor(t),i.lineWidth=s,i.stroke());const r=this.getNodeLabel(t);if(r&&this.shouldShowLabel(t,o)){const p=typeof this.options.nodeLabelColor=="function"?this.options.nodeLabelColor(t):this.options.nodeLabelColor??"#555",n=(this.options.labelFontSize||14)/o;i.font=`${n}px Arial`,i.fillStyle=p,i.textAlign="center",i.textBaseline="middle",i.fillText(r,t.x||0,(t.y||0)+e+n/2+2)}}),this.applyLinkOptions()}getNodeSize(t){return typeof this.options.nodeSize=="function"?this.options.nodeSize(t)||t?.marker?.radius:this.options.nodeSize||t?.marker?.radius||1}getNodeLabel(t){return typeof this.options.nodeLabel=="function"?this.options.nodeLabel(t):t?.label||t.id}shouldShowLabel(t,i){const o=this.getNodeSize(t)*2,s=(this.options.labelFontSize||14)/i,r=this.options.labelThreshold||1.5;return s<=o*r}updateData(t){const i=new Set(this.data.nodes.map(r=>r.id.toString())),o=t.nodes.filter(r=>!i.has(r.id.toString()));o.forEach(r=>{this.nodesMap.set(r.id.toString(),r)});const e=new Set(this.data.links.map(r=>this.createLinkKey(r.source,r.target))),s=t.links.filter(r=>!e.has(this.createLinkKey(r.source,r.target)));this.data={nodes:[...this.data.nodes,...o],links:[...this.data.links,...s]},this.refreshGraph()}getNodeColor(t){if(typeof this.options.nodeColor=="function"){const i=this.options.nodeColor(t);if(i)return i}return t?.color??""}getNodeBorderColor(t){return typeof this.options.nodeBorderColor=="function"?this.options.nodeBorderColor(t):this.options.nodeBorderColor||"#333"}applyLinkOptions(){this.graph.linkColor(t=>typeof this.options.linkColor=="function"?this.options.linkColor(t):this.options.linkColor?this.options.linkColor:t.color??""),this.options.linkWidth!==void 0&&this.graph.linkWidth(t=>this.getLinkProperty(this.options.linkWidth,t)??1),this.options.linkCurvature!==void 0&&this.graph.linkCurvature(this.getLinkCurvature.bind(this)),this.options.linkDirectionalParticles&&this.graph.linkDirectionalParticles(t=>this.getLinkProperty(this.options.linkDirectionalParticles,t)??0),this.options.linkDirectionalParticleSpeed!==void 0&&this.graph.linkDirectionalParticleSpeed(t=>this.getLinkProperty(this.options.linkDirectionalParticleSpeed,t)??0),this.options.linkDirectionalParticleWidth!==void 0&&this.graph.linkDirectionalParticleWidth(t=>this.getLinkProperty(this.options.linkDirectionalParticleWidth,t)??0),this.options.linkDirectionalParticleColor!==void 0&&this.graph.linkDirectionalParticleColor(t=>this.getLinkProperty(this.options.linkDirectionalParticleColor,t)??"#aaa")}getLinkCurvature(t){return typeof this.options.linkCurvature=="function"?this.options.linkCurvature(t):typeof this.options.linkCurvature=="string"?t[this.options.linkCurvature]||0:typeof this.options.linkCurvature=="number"?this.options.linkCurvature:t.curvature||0}getLinkProperty(t,i){return typeof t=="function"?t(i):t}calculateGroupBounds(){if(!this.options.showGroups)return;this.groupBounds.clear();const t=this.options.groupPadding||20,i=new Map;this.data.nodes.forEach(o=>{const e=this.getNodeGroupId(o);e&&(i.has(e)||i.set(e,[]),i.get(e).push(o))}),i.forEach((o,e)=>{if(o.length===0)return;let s=1/0,r=1/0,p=-1/0,u=-1/0;o.forEach(n=>{if(n.x!==void 0&&n.y!==void 0){const a=this.getNodeSize(n);s=Math.min(s,n.x-a),r=Math.min(r,n.y-a),p=Math.max(p,n.x+a),u=Math.max(u,n.y+a)}}),s!==1/0&&this.groupBounds.set(e,{minX:s-t,minY:r-t,maxX:p+t,maxY:u+t,nodes:o})})}getNodeGroupId(t){if(this.options.groupBy){if(typeof this.options.groupBy=="function")return this.options.groupBy(t);if(typeof this.options.groupBy=="string")return t[this.options.groupBy]}}renderGroups(t,i){this.options.showGroups&&(this.calculateGroupBounds(),this.groupBounds.forEach((o,e)=>{const s=this.getGroupBorderColor(e),r=this.options.groupBorderWidth||2,p=this.options.groupBorderOpacity||.3;t.save(),t.globalAlpha=p,t.strokeStyle=s,t.lineWidth=r/i,t.setLineDash([10/i,5/i]),t.strokeRect(o.minX,o.minY,o.maxX-o.minX,o.maxY-o.minY),t.restore();const u=this.options.groupLabelThreshold||.8;if(i<=u){const n=this.getGroupLabelColor(e),a=(this.options.groupLabelSize||16)/i;t.save(),t.font=`bold ${a}px Arial`,t.fillStyle=n,t.textAlign="center",t.textBaseline="middle";const g=(o.minX+o.maxX)/2,f=o.minY-a/2,c=t.measureText(e).width,y=a;t.globalAlpha=.8,t.fillStyle="rgba(255, 255, 255, 0.9)",t.fillRect(g-c/2-4,f-y/2-2,c+8,y+4),t.globalAlpha=1,t.fillStyle=n,t.fillText(e,g,f),t.restore()}}))}getGroupBorderColor(t){return typeof this.options.groupBorderColor=="function"?this.options.groupBorderColor(t):this.options.groupBorderColor||"#666"}getGroupLabelColor(t){return typeof this.options.groupLabelColor=="function"?this.options.groupLabelColor(t):this.options.groupLabelColor||"#333"}getNodeById(t){return this.nodesMap.get(t.toString())}hasNode(t){return this.nodesMap.has(t.toString())}getCooldownTime(){if(this.options.cooldownTime!==void 0)return this.options.cooldownTime;const i=this.data.nodes.length*(125/100);return Math.max(4e3,i)}getDataSize(){const{nodes:t,links:i}=this.graph.graphData();return{nodes:t.length,links:i.length}}getAllNodeIds(){return Array.from(this.nodesMap.keys())}updateNode(t,i){const o=this.nodesMap.get(t.toString());if(o){Object.assign(o,i);const e=this.data.nodes.findIndex(s=>s.id.toString()===t.toString());return e!==-1&&(this.data.nodes[e]=o),this.refreshGraph(),!0}return!1}removeNode(t){const i=t.toString();return this.nodesMap.has(i)?(this.nodesMap.delete(i),this.data.nodes=this.data.nodes.filter(o=>o.id.toString()!==i),this.data.links=this.data.links.filter(o=>{const e=typeof o.source=="object"?o.source.id:o.source,s=typeof o.target=="object"?o.target.id:o.target;return e?.toString()!==i&&s?.toString()!==i}),this.graph.cooldownTime(this.getCooldownTime()),this.refreshGraph(),!0):!1}async addData(t){t.nodes.forEach(i=>{this.nodesMap.has(i.id.toString())||this.nodesMap.set(i.id.toString(),i)}),t.links.forEach(i=>{const o=this.createLinkKey(i.source,i.target);this.linkMap.has(o)?console.log("link already exists",i):this.linkMap.set(o,i)}),this.data={nodes:Array.from(this.nodesMap.values()),links:Array.from(this.linkMap.values())},this.graph.cooldownTime(this.getCooldownTime()),this.graph.graphData(this.data)}setLabelThreshold(t){this.options.labelThreshold=t,this.render()}setOptions(t){this.options={...this.options,...t},this.applyOptions()}refreshGraph(){this.graphData(this.data)}reinitialize(){this.initGraph()}reset(){this.graph.pauseAnimation();const t=this.graph.d3Force("simulation");t&&t.stop(),this.data={nodes:[],links:[]},this.nodesMap.clear(),this.graph=new l(this.container),this.initGraph()}getData(){return this.data}getNodesData(){return this.data.nodes}getLinksData(){return this.data.links}createLinkKey(t,i){const o=t===void 0?"undefined":typeof t=="object"?t.id:t,e=i===void 0?"undefined":typeof i=="object"?i.id:i;return`${o}-${e}`}graphData(t){return this.nodesMap.clear(),this.linkMap.clear(),t.nodes.forEach(i=>{this.nodesMap.has(i.id.toString())||this.nodesMap.set(i.id.toString(),i)}),t.links.forEach(i=>{const o=this.createLinkKey(i.source,i.target);this.linkMap.has(o)||this.linkMap.set(o,i)}),this.data={nodes:Array.from(this.nodesMap.values()),links:Array.from(this.linkMap.values())},this.graph.cooldownTime(this.getCooldownTime()),this.graph.graphData(this.data),this}showGroups(t){return this.options.showGroups=t,t&&Object.entries({groupBy:"topic",groupBorderColor:"#666",groupBorderWidth:2,groupBorderOpacity:.3,groupLabelColor:"#333",groupLabelSize:16,groupLabelThreshold:.8,groupPadding:20}).forEach(([o,e])=>{this.options[o]===void 0&&(this.options[o]=e)}),this.applyOptions(),this.refreshGraph(),this}setGroupBy(t){return this.options.groupBy=t,this.applyOptions(),this.refreshGraph(),this}setGroupOptions(t){return t.borderColor!==void 0&&(this.options.groupBorderColor=t.borderColor),t.borderWidth!==void 0&&(this.options.groupBorderWidth=t.borderWidth),t.borderOpacity!==void 0&&(this.options.groupBorderOpacity=t.borderOpacity),t.labelColor!==void 0&&(this.options.groupLabelColor=t.labelColor),t.labelSize!==void 0&&(this.options.groupLabelSize=t.labelSize),t.labelThreshold!==void 0&&(this.options.groupLabelThreshold=t.labelThreshold),t.padding!==void 0&&(this.options.groupPadding=t.padding),this.applyOptions(),this.refreshGraph(),this}getGroups(){const t=new Set;return this.data.nodes.forEach(i=>{const o=this.getNodeGroupId(i);o&&t.add(o)}),Array.from(t)}getNodesInGroup(t){return this.data.nodes.filter(i=>this.getNodeGroupId(i)===t)}getOptions(){return{...this.options}}destroy(){this.worker&&(this.worker.terminate(),this.worker=null),this.graph._destructor()}}h.ForceGraph=C,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(d,u){typeof exports=="object"&&typeof module<"u"?u(exports,require("force-graph"),require("d3")):typeof define=="function"&&define.amd?define(["exports","force-graph","d3"],u):(d=typeof globalThis<"u"?globalThis:d||self,u(d.ForceGraphLib={},d.ForceGraph))})(this,(function(d,u){"use strict";class k{container;graph;data={nodes:[],links:[]};nodesMap=new Map;linkMap=new Map;options;worker=null;groupBounds=new Map;isFirstRender=!0;constructor(t,o={nodes:[],links:[]},i={}){this.container=t,this.data=o,this.graph=new u(this.container),o.nodes.forEach(r=>{this.nodesMap.set(r.id.toString(),r)});const e={labelThreshold:1.5,showGroups:!1,...i},s=e.showGroups?{groupBy:"topic",groupBorderColor:"#666",groupBorderWidth:2,groupBorderOpacity:.3,groupLabelColor:"#333",groupLabelSize:16,groupLabelThreshold:.8,groupPadding:20}:{};this.options={...e,...s},this.initGraph()}initGraph(){this.applyOptions(),this.render(),this.graphData(this.data),this.graph.onEngineStop(()=>{if(!this.data.nodes.length){this.isFirstRender=!0;return}this.isFirstRender?(this.options.onRenderComplete?this.options.onRenderComplete():this.graph.zoomToFit(400),this.isFirstRender=!1):this.options.onGraphUpdated&&this.options.onGraphUpdated()})}renderer(){return this.graph}focusPosition(t={}){if(Object.values(t).length){if(t.id){const{x:o,y:i}=this.nodesMap.get(t.id)??{x:0,y:0};o&&i&&(t={x:o,y:i})}t&&(this.graph.centerAt(t.x,t.y,1e3),this.graph.zoom(8,2e3))}}render(){this.graph.width(this.options.width??800).height(this.options.height??400),this.options.cooldownTicks!==void 0&&this.graph.cooldownTicks(this.options.cooldownTicks)}force(t,o){return this.graph.d3Force(t,o)}applyOptions(){this.options.keepDragPosition&&this.graph.onNodeDragEnd(t=>{t.fx=t.x,t.fy=t.y}),this.options.pointerInteraction||this.graph.enablePointerInteraction(!1),this.graph.nodeCanvasObject((t,o,i)=>{t===this.data.nodes[0]&&this.renderGroups(o,i);const e=this.getNodeSize(t)*2,s=typeof this.options.nodeBorderWidth=="function"?this.options.nodeBorderWidth(t):this.options.nodeBorderWidth,r=this.resolveNodeShape(t),a=t.x||0,h=t.y||0;this.drawNodeShape(o,a,h,e,r),o.fillStyle=this.getNodeColor(t),o.fill(),s&&s>0&&(this.drawNodeShape(o,a,h,e,r),o.strokeStyle=this.getNodeBorderColor(t),o.lineWidth=s,o.stroke());const n=this.getNodeLabel(t);if(n&&this.shouldShowLabel(t,i)){const p=typeof this.options.nodeLabelColor=="function"?this.options.nodeLabelColor(t):this.options.nodeLabelColor??"#555",l=(this.options.labelFontSize||14)/i;o.font=`${l}px Arial`,o.fillStyle=p,o.textAlign="center",o.textBaseline="middle",o.fillText(n,t.x||0,(t.y||0)+e+l/2+2)}}),this.applyLinkOptions()}getNodeSize(t){return typeof this.options.nodeSize=="function"?this.options.nodeSize(t)||t?.marker?.radius:this.options.nodeSize||t?.marker?.radius||1}getNodeLabel(t){return typeof this.options.nodeLabel=="function"?this.options.nodeLabel(t):t?.label||t.id}shouldShowLabel(t,o){const i=this.getNodeSize(t)*2,s=(this.options.labelFontSize||14)/o,r=this.options.labelThreshold||1.5;return s<=i*r}updateData(t){const o=new Set(this.data.nodes.map(r=>r.id.toString())),i=t.nodes.filter(r=>!o.has(r.id.toString()));i.forEach(r=>{this.nodesMap.set(r.id.toString(),r)});const e=new Set(this.data.links.map(r=>this.createLinkKey(r.source,r.target))),s=t.links.filter(r=>!e.has(this.createLinkKey(r.source,r.target)));this.data={nodes:[...this.data.nodes,...i],links:[...this.data.links,...s]},this.refreshGraph()}getNodeColor(t){if(typeof this.options.nodeColor=="function"){const o=this.options.nodeColor(t);if(o)return o}return t?.color??""}getNodeBorderColor(t){return typeof this.options.nodeBorderColor=="function"?this.options.nodeBorderColor(t):this.options.nodeBorderColor||"#333"}resolveNodeShape(t){return t.shape?t.shape:typeof this.options.nodeShape=="function"?this.options.nodeShape(t):this.options.nodeShape??"circle"}drawNodeShape(t,o,i,e,s){switch(t.beginPath(),s){case"square":{t.rect(o-e,i-e,e*2,e*2);break}case"triangle":{const r=e*Math.sqrt(3);t.moveTo(o,i-e),t.lineTo(o+r/2,i+e/2),t.lineTo(o-r/2,i+e/2),t.closePath();break}case"diamond":{t.moveTo(o,i-e),t.lineTo(o+e,i),t.lineTo(o,i+e),t.lineTo(o-e,i),t.closePath();break}case"star":{const r=e,a=e*.4,h=5;for(let n=0;n<h*2;n++){const p=n%2===0?r:a,f=Math.PI/h*n-Math.PI/2,l=o+p*Math.cos(f),g=i+p*Math.sin(f);n===0?t.moveTo(l,g):t.lineTo(l,g)}t.closePath();break}case"hexagon":{for(let r=0;r<6;r++){const a=Math.PI/3*r-Math.PI/6,h=o+e*Math.cos(a),n=i+e*Math.sin(a);r===0?t.moveTo(h,n):t.lineTo(h,n)}t.closePath();break}case"circle":default:t.arc(o,i,e,0,2*Math.PI);break}}applyLinkOptions(){this.graph.linkColor(t=>typeof this.options.linkColor=="function"?this.options.linkColor(t):this.options.linkColor?this.options.linkColor:t.color??""),this.options.linkWidth!==void 0&&this.graph.linkWidth(t=>this.getLinkProperty(this.options.linkWidth,t)??1),this.options.linkCurvature!==void 0&&this.graph.linkCurvature(this.getLinkCurvature.bind(this)),this.options.linkDirectionalParticles&&this.graph.linkDirectionalParticles(t=>this.getLinkProperty(this.options.linkDirectionalParticles,t)??0),this.options.linkDirectionalParticleSpeed!==void 0&&this.graph.linkDirectionalParticleSpeed(t=>this.getLinkProperty(this.options.linkDirectionalParticleSpeed,t)??0),this.options.linkDirectionalParticleWidth!==void 0&&this.graph.linkDirectionalParticleWidth(t=>this.getLinkProperty(this.options.linkDirectionalParticleWidth,t)??0),this.options.linkDirectionalParticleColor!==void 0&&this.graph.linkDirectionalParticleColor(t=>this.getLinkProperty(this.options.linkDirectionalParticleColor,t)??"#aaa")}getLinkCurvature(t){return typeof this.options.linkCurvature=="function"?this.options.linkCurvature(t):typeof this.options.linkCurvature=="string"?t[this.options.linkCurvature]||0:typeof this.options.linkCurvature=="number"?this.options.linkCurvature:t.curvature||0}getLinkProperty(t,o){return typeof t=="function"?t(o):t}calculateGroupBounds(){if(!this.options.showGroups)return;this.groupBounds.clear();const t=this.options.groupPadding||20,o=new Map;this.data.nodes.forEach(i=>{const e=this.getNodeGroupId(i);e&&(o.has(e)||o.set(e,[]),o.get(e).push(i))}),o.forEach((i,e)=>{if(i.length===0)return;let s=1/0,r=1/0,a=-1/0,h=-1/0;i.forEach(n=>{if(n.x!==void 0&&n.y!==void 0){const p=this.getNodeSize(n);s=Math.min(s,n.x-p),r=Math.min(r,n.y-p),a=Math.max(a,n.x+p),h=Math.max(h,n.y+p)}}),s!==1/0&&this.groupBounds.set(e,{minX:s-t,minY:r-t,maxX:a+t,maxY:h+t,nodes:i})})}getNodeGroupId(t){if(this.options.groupBy){if(typeof this.options.groupBy=="function")return this.options.groupBy(t);if(typeof this.options.groupBy=="string")return t[this.options.groupBy]}}renderGroups(t,o){this.options.showGroups&&(this.calculateGroupBounds(),this.groupBounds.forEach((i,e)=>{const s=this.getGroupBorderColor(e),r=this.options.groupBorderWidth||2,a=this.options.groupBorderOpacity||.3;t.save(),t.globalAlpha=a,t.strokeStyle=s,t.lineWidth=r/o,t.setLineDash([10/o,5/o]),t.strokeRect(i.minX,i.minY,i.maxX-i.minX,i.maxY-i.minY),t.restore();const h=this.options.groupLabelThreshold||.8;if(o<=h){const n=this.getGroupLabelColor(e),p=(this.options.groupLabelSize||16)/o;t.save(),t.font=`bold ${p}px Arial`,t.fillStyle=n,t.textAlign="center",t.textBaseline="middle";const f=(i.minX+i.maxX)/2,l=i.minY-p/2,c=t.measureText(e).width,y=p;t.globalAlpha=.8,t.fillStyle="rgba(255, 255, 255, 0.9)",t.fillRect(f-c/2-4,l-y/2-2,c+8,y+4),t.globalAlpha=1,t.fillStyle=n,t.fillText(e,f,l),t.restore()}}))}getGroupBorderColor(t){return typeof this.options.groupBorderColor=="function"?this.options.groupBorderColor(t):this.options.groupBorderColor||"#666"}getGroupLabelColor(t){return typeof this.options.groupLabelColor=="function"?this.options.groupLabelColor(t):this.options.groupLabelColor||"#333"}getNodeById(t){return this.nodesMap.get(t.toString())}hasNode(t){return this.nodesMap.has(t.toString())}getCooldownTime(){if(this.options.cooldownTime!==void 0)return this.options.cooldownTime;const o=this.data.nodes.length*(125/100);return Math.max(4e3,o)}getDataSize(){const{nodes:t,links:o}=this.graph.graphData();return{nodes:t.length,links:o.length}}getAllNodeIds(){return Array.from(this.nodesMap.keys())}updateNode(t,o){const i=this.nodesMap.get(t.toString());if(i){Object.assign(i,o);const e=this.data.nodes.findIndex(s=>s.id.toString()===t.toString());return e!==-1&&(this.data.nodes[e]=i),this.refreshGraph(),!0}return!1}removeNode(t){const o=t.toString();return this.nodesMap.has(o)?(this.nodesMap.delete(o),this.data.nodes=this.data.nodes.filter(i=>i.id.toString()!==o),this.data.links=this.data.links.filter(i=>{const e=typeof i.source=="object"?i.source.id:i.source,s=typeof i.target=="object"?i.target.id:i.target;return e?.toString()!==o&&s?.toString()!==o}),this.refreshGraph(),!0):!1}async addData(t){t.nodes.forEach(o=>{this.nodesMap.has(o.id.toString())||this.nodesMap.set(o.id.toString(),o)}),t.links.forEach(o=>{const i=this.createLinkKey(o.source,o.target);this.linkMap.has(i)?console.log("link already exists",o):this.linkMap.set(i,o)}),this.data={nodes:Array.from(this.nodesMap.values()),links:Array.from(this.linkMap.values())},this.graph.cooldownTime(this.getCooldownTime()),this.graph.graphData(this.data)}setLabelThreshold(t){this.options.labelThreshold=t,this.render()}setOptions(t){this.options={...this.options,...t},this.applyOptions()}refreshGraph(){this.graphData(this.data)}reinitialize(){this.initGraph()}reset(){this.graph.pauseAnimation(),this.data={nodes:[],links:[]},this.nodesMap.clear(),this.graph=new u(this.container),this.initGraph()}getData(){return this.data}getNodesData(){return this.data.nodes}getLinksData(){return this.data.links}createLinkKey(t,o){const i=t===void 0?"undefined":typeof t=="object"?t.id:t,e=o===void 0?"undefined":typeof o=="object"?o.id:o;return`${i}-${e}`}graphData(t){return this.nodesMap.clear(),this.linkMap.clear(),t.nodes.forEach(o=>{this.nodesMap.has(o.id.toString())||this.nodesMap.set(o.id.toString(),o)}),t.links.forEach(o=>{const i=this.createLinkKey(o.source,o.target);this.linkMap.has(i)||this.linkMap.set(i,o)}),this.data={nodes:Array.from(this.nodesMap.values()),links:Array.from(this.linkMap.values())},this.graph.cooldownTime(this.getCooldownTime()),this.graph.graphData(this.data),this}showGroups(t){return this.options.showGroups=t,t&&Object.entries({groupBy:"topic",groupBorderColor:"#666",groupBorderWidth:2,groupBorderOpacity:.3,groupLabelColor:"#333",groupLabelSize:16,groupLabelThreshold:.8,groupPadding:20}).forEach(([i,e])=>{this.options[i]===void 0&&(this.options[i]=e)}),this.applyOptions(),this.refreshGraph(),this}setGroupBy(t){return this.options.groupBy=t,this.applyOptions(),this.refreshGraph(),this}setGroupOptions(t){return t.borderColor!==void 0&&(this.options.groupBorderColor=t.borderColor),t.borderWidth!==void 0&&(this.options.groupBorderWidth=t.borderWidth),t.borderOpacity!==void 0&&(this.options.groupBorderOpacity=t.borderOpacity),t.labelColor!==void 0&&(this.options.groupLabelColor=t.labelColor),t.labelSize!==void 0&&(this.options.groupLabelSize=t.labelSize),t.labelThreshold!==void 0&&(this.options.groupLabelThreshold=t.labelThreshold),t.padding!==void 0&&(this.options.groupPadding=t.padding),this.applyOptions(),this.refreshGraph(),this}getGroups(){const t=new Set;return this.data.nodes.forEach(o=>{const i=this.getNodeGroupId(o);i&&t.add(i)}),Array.from(t)}getNodesInGroup(t){return this.data.nodes.filter(o=>this.getNodeGroupId(o)===t)}getOptions(){return{...this.options}}destroy(){this.worker&&(this.worker.terminate(),this.worker=null),this.graph._destructor()}}d.ForceGraph=k,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
package/dist/index.d.ts CHANGED
@@ -62,6 +62,8 @@ export declare class ForceGraph<TNode extends NodeData & {
62
62
  updateData(data: GraphData<TNode, TLink>): void;
63
63
  private getNodeColor;
64
64
  private getNodeBorderColor;
65
+ private resolveNodeShape;
66
+ private drawNodeShape;
65
67
  private applyLinkOptions;
66
68
  private getLinkCurvature;
67
69
  private getLinkProperty;
@@ -177,6 +179,7 @@ export declare interface GraphOptions<N extends NodeData = NodeData, L extends L
177
179
  nodeColor?: string | ((node: N) => string);
178
180
  nodeBorderColor?: string | ((node: N) => string);
179
181
  nodeBorderWidth?: number | ((node: N) => number);
182
+ nodeShape?: NodeShape | ((node: N) => NodeShape);
180
183
  nodeGap?: number;
181
184
  linkLabel?: string | ((link: L) => string);
182
185
  nodeIcon?: string | ((node: N) => string);
@@ -225,7 +228,10 @@ export declare interface NodeData extends NodeObject {
225
228
  platform?: string;
226
229
  type?: string;
227
230
  topic?: string;
231
+ shape?: NodeShape;
228
232
  [key: string]: any;
229
233
  }
230
234
 
235
+ declare type NodeShape = 'circle' | 'square' | 'triangle' | 'diamond' | 'star' | 'hexagon';
236
+
231
237
  export { }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@logixode/force-graph-lib",
3
3
  "private": false,
4
- "version": "0.4.1",
4
+ "version": "0.5.1",
5
5
  "description": "Force-directed graph visualization library.",
6
6
  "author": "Rohmad Kurniadi",
7
7
  "license": "MIT",
@@ -36,6 +36,7 @@
36
36
  "scripts": {
37
37
  "dev": "bunx --bun vite",
38
38
  "build": "bunx --bun vite build && npx tsc --project tsconfig.lib.json --noEmit",
39
+ "type-check": "tsc --project tsconfig.lib.json --noEmit",
39
40
  "preview": "bunx --bun vite preview",
40
41
  "docs:dev": "bunx --bun vitepress dev",
41
42
  "docs:build": "vitepress build",