@logixode/force-graph-lib 0.5.1 → 0.6.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 c from "force-graph";
2
- import "d3";
3
- class C {
1
+ import y from "force-graph";
2
+ import * as g from "d3";
3
+ class S {
4
4
  container;
5
5
  graph;
6
6
  data = { nodes: [], links: [] };
@@ -8,10 +8,12 @@ class C {
8
8
  linkMap = /* @__PURE__ */ new Map();
9
9
  options;
10
10
  worker = null;
11
- groupBounds = /* @__PURE__ */ new Map();
11
+ groupHulls = /* @__PURE__ */ new Map();
12
+ nodeGroupsCache = null;
12
13
  isFirstRender = !0;
14
+ autoColorScale = g.scaleOrdinal(g.schemeCategory10);
13
15
  constructor(t, o = { nodes: [], links: [] }, i = {}) {
14
- this.container = t, this.data = o, this.graph = new c(this.container), o.nodes.forEach((r) => {
16
+ this.container = t, this.data = o, this.graph = new y(this.container), o.nodes.forEach((r) => {
15
17
  this.nodesMap.set(r.id.toString(), r);
16
18
  });
17
19
  const e = {
@@ -21,17 +23,18 @@ class C {
21
23
  ...i
22
24
  }, s = e.showGroups ? {
23
25
  groupBy: "topic",
26
+ groupFillColor: "rgba(0, 0, 0, 0.05)",
27
+ groupFillOpacity: 1,
24
28
  groupBorderColor: "#666",
25
29
  groupBorderWidth: 2,
26
30
  groupBorderOpacity: 0.3,
27
31
  groupLabelColor: "#333",
28
32
  groupLabelSize: 16,
29
- groupLabelThreshold: 0.8,
30
33
  groupPadding: 20
31
34
  } : {};
32
35
  this.options = {
33
- ...e,
34
- ...s
36
+ ...s,
37
+ ...e
35
38
  }, this.initGraph();
36
39
  }
37
40
  initGraph() {
@@ -41,6 +44,8 @@ class C {
41
44
  return;
42
45
  }
43
46
  this.isFirstRender ? (this.options.onRenderComplete ? this.options.onRenderComplete() : this.graph.zoomToFit(400), this.isFirstRender = !1) : this.options.onGraphUpdated && this.options.onGraphUpdated();
47
+ }), this.graph.onEngineTick(() => {
48
+ this.calculateGroupHulls();
44
49
  });
45
50
  }
46
51
  renderer() {
@@ -66,12 +71,12 @@ class C {
66
71
  t.fx = t.x, t.fy = t.y;
67
72
  }), this.options.pointerInteraction || this.graph.enablePointerInteraction(!1), this.graph.nodeCanvasObject((t, o, i) => {
68
73
  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);
74
+ const e = this.getNodeSize(t) * 2, s = typeof this.options.nodeBorderWidth == "function" ? this.options.nodeBorderWidth(t) : this.options.nodeBorderWidth, r = this.resolveNodeShape(t), l = t.x || 0, n = t.y || 0;
75
+ this.drawNodeShape(o, l, n, e, r), o.fillStyle = this.getNodeColor(t), o.fill(), s && s > 0 && (this.drawNodeShape(o, l, n, e, r), o.strokeStyle = this.getNodeBorderColor(t), o.lineWidth = s, o.stroke());
76
+ const a = this.getNodeLabel(t);
77
+ if (a && this.shouldShowLabel(t, i)) {
78
+ const h = typeof this.options.nodeLabelColor == "function" ? this.options.nodeLabelColor(t) : this.options.nodeLabelColor ?? "#555", d = (this.options.labelFontSize || 14) / i;
79
+ o.font = `${d}px Arial`, o.fillStyle = h, o.textAlign = "center", o.textBaseline = "middle", o.fillText(a, t.x || 0, (t.y || 0) + e + d / 2 + 2);
75
80
  }
76
81
  }), this.applyLinkOptions();
77
82
  }
@@ -133,18 +138,18 @@ class C {
133
138
  break;
134
139
  }
135
140
  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);
141
+ const r = e, l = e * 0.4, n = 5;
142
+ for (let a = 0; a < n * 2; a++) {
143
+ const h = a % 2 === 0 ? r : l, p = Math.PI / n * a - Math.PI / 2, d = o + h * Math.cos(p), u = i + h * Math.sin(p);
144
+ a === 0 ? t.moveTo(d, u) : t.lineTo(d, u);
140
145
  }
141
146
  t.closePath();
142
147
  break;
143
148
  }
144
149
  case "hexagon": {
145
150
  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);
151
+ const l = Math.PI / 3 * r - Math.PI / 6, n = o + e * Math.cos(l), a = i + e * Math.sin(l);
152
+ r === 0 ? t.moveTo(n, a) : t.lineTo(n, a);
148
153
  }
149
154
  t.closePath();
150
155
  break;
@@ -175,30 +180,40 @@ class C {
175
180
  return typeof t == "function" ? t(o) : t;
176
181
  }
177
182
  /**
178
- * Calculate group boundaries based on node positions
183
+ * Calculate group convex hulls based on node positions
179
184
  */
180
- calculateGroupBounds() {
185
+ calculateGroupHulls() {
181
186
  if (!this.options.showGroups) return;
182
- this.groupBounds.clear();
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) => {
187
+ this.groupHulls.clear();
188
+ const t = this.options.groupPadding || 20;
189
+ this.ensureNodeGroupsCache(), this.nodeGroupsCache.forEach((i, e) => {
188
190
  if (i.length === 0) return;
189
- let s = 1 / 0, r = 1 / 0, a = -1 / 0, h = -1 / 0;
190
- i.forEach((n) => {
191
+ const s = [];
192
+ let r = 1 / 0, l = 0;
193
+ if (i.forEach((n) => {
191
194
  if (n.x !== void 0 && n.y !== void 0) {
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);
195
+ const h = this.getNodeSize(n) + t;
196
+ for (let p = 0; p < 8; p++) {
197
+ const d = p * Math.PI / 4;
198
+ s.push([
199
+ n.x + h * Math.cos(d),
200
+ n.y + h * Math.sin(d)
201
+ ]);
202
+ }
203
+ n.y - h < r && (r = n.y - h, l = n.x);
194
204
  }
195
- }), s !== 1 / 0 && this.groupBounds.set(e, {
196
- minX: s - t,
197
- minY: r - t,
198
- maxX: a + t,
199
- maxY: h + t,
200
- nodes: i
201
- });
205
+ }), s.length >= 3) {
206
+ const n = g.polygonHull(s);
207
+ if (n) {
208
+ const a = n.filter((h) => Math.abs(h[1] - r) < t);
209
+ a.length > 0 && (l = a.reduce((h, p) => h + p[0], 0) / a.length), this.groupHulls.set(e, {
210
+ points: n,
211
+ labelX: l,
212
+ labelY: r,
213
+ nodes: i
214
+ });
215
+ }
216
+ }
202
217
  });
203
218
  }
204
219
  /**
@@ -213,31 +228,47 @@ class C {
213
228
  }
214
229
  }
215
230
  /**
216
- * Render group borders and labels
231
+ * Populate group cache to prevent calculating on every frame
217
232
  */
218
- renderGroups(t, o) {
219
- this.options.showGroups && (this.calculateGroupBounds(), this.groupBounds.forEach((i, e) => {
220
- const s = this.getGroupBorderColor(e), r = this.options.groupBorderWidth || 2, a = this.options.groupBorderOpacity || 0.3;
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;
227
- t.globalAlpha = 0.8, t.fillStyle = "rgba(255, 255, 255, 0.9)", t.fillRect(
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();
233
- }
233
+ ensureNodeGroupsCache() {
234
+ this.nodeGroupsCache || (this.nodeGroupsCache = /* @__PURE__ */ new Map(), this.data.nodes.forEach((t) => {
235
+ const o = this.getNodeGroupId(t);
236
+ o && (this.nodeGroupsCache.has(o) || this.nodeGroupsCache.set(o, []), this.nodeGroupsCache.get(o).push(t));
234
237
  }));
235
238
  }
239
+ /**
240
+ * Render group hulls and labels
241
+ */
242
+ renderGroups(t, o) {
243
+ if (!this.options.showGroups) return;
244
+ const i = g.line().curve(g.curveCatmullRomClosed).context(t);
245
+ this.groupHulls.forEach((e, s) => {
246
+ const r = this.getGroupFillColor(s), l = this.options.groupFillOpacity ?? 1, n = this.getGroupBorderColor(s), a = this.options.groupBorderWidth ?? 0, h = this.options.groupBorderOpacity ?? 0.3;
247
+ t.save(), t.beginPath(), i(e.points), r && (t.globalAlpha = l, t.fillStyle = r, t.fill()), a > 0 && n && (t.globalAlpha = h, t.strokeStyle = n, t.lineWidth = a / o, t.setLineDash([]), t.stroke()), t.restore();
248
+ const p = this.getGroupLabelColor(s), d = (this.options.groupLabelSize || 16) / o;
249
+ t.save(), t.font = `bold ${d}px Arial`, t.fillStyle = p, t.textAlign = "center", t.textBaseline = "middle";
250
+ const u = e.labelY - d / 2, f = t.measureText(s).width, c = d;
251
+ t.globalAlpha = 0.8, t.fillStyle = "rgba(255, 255, 255, 0.9)", t.fillRect(
252
+ e.labelX - f / 2 - 4,
253
+ u - c / 2 - 2,
254
+ f + 8,
255
+ c + 4
256
+ ), t.globalAlpha = 1, t.fillStyle = p, t.fillText(s, e.labelX, u), t.restore();
257
+ });
258
+ }
259
+ /**
260
+ * Get group fill color
261
+ */
262
+ getGroupFillColor(t) {
263
+ let o;
264
+ return typeof this.options.groupFillColor == "function" ? o = this.options.groupFillColor(t) : o = this.options.groupFillColor, o === "auto" ? this.autoColorScale(t) : o;
265
+ }
236
266
  /**
237
267
  * Get group border color
238
268
  */
239
269
  getGroupBorderColor(t) {
240
- return typeof this.options.groupBorderColor == "function" ? this.options.groupBorderColor(t) : this.options.groupBorderColor || "#666";
270
+ let o;
271
+ return typeof this.options.groupBorderColor == "function" ? o = this.options.groupBorderColor(t) : o = this.options.groupBorderColor, o === "auto" ? this.autoColorScale(t) : o || "#666";
241
272
  }
242
273
  /**
243
274
  * Get group label color
@@ -322,7 +353,7 @@ class C {
322
353
  this.initGraph();
323
354
  }
324
355
  reset() {
325
- this.graph.pauseAnimation(), this.data = { nodes: [], links: [] }, this.nodesMap.clear(), this.graph = new c(this.container), this.initGraph();
356
+ this.graph.pauseAnimation(), this.data = { nodes: [], links: [] }, this.nodesMap.clear(), this.graph = new y(this.container), this.initGraph();
326
357
  }
327
358
  getData() {
328
359
  return this.data;
@@ -342,7 +373,7 @@ class C {
342
373
  * @param data - Graph data to set
343
374
  */
344
375
  graphData(t) {
345
- return this.nodesMap.clear(), this.linkMap.clear(), t.nodes.forEach((o) => {
376
+ return this.nodesMap.clear(), this.linkMap.clear(), this.nodeGroupsCache = null, t.nodes.forEach((o) => {
346
377
  this.nodesMap.has(o.id.toString()) || this.nodesMap.set(o.id.toString(), o);
347
378
  }), t.links.forEach((o) => {
348
379
  const i = this.createLinkKey(o.source, o.target);
@@ -358,12 +389,13 @@ class C {
358
389
  showGroups(t) {
359
390
  return this.options.showGroups = t, t && Object.entries({
360
391
  groupBy: "topic",
392
+ groupFillColor: "rgba(0, 0, 0, 0.05)",
393
+ groupFillOpacity: 1,
361
394
  groupBorderColor: "#666",
362
395
  groupBorderWidth: 2,
363
396
  groupBorderOpacity: 0.3,
364
397
  groupLabelColor: "#333",
365
398
  groupLabelSize: 16,
366
- groupLabelThreshold: 0.8,
367
399
  groupPadding: 20
368
400
  }).forEach(([i, e]) => {
369
401
  this.options[i] === void 0 && (this.options[i] = e);
@@ -379,7 +411,7 @@ class C {
379
411
  * Set group visualization options
380
412
  */
381
413
  setGroupOptions(t) {
382
- 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;
414
+ return t.fillColor !== void 0 && (this.options.groupFillColor = t.fillColor), t.fillOpacity !== void 0 && (this.options.groupFillOpacity = t.fillOpacity), 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.padding !== void 0 && (this.options.groupPadding = t.padding), this.applyOptions(), this.refreshGraph(), this;
383
415
  }
384
416
  /**
385
417
  * Get all available groups
@@ -408,5 +440,5 @@ class C {
408
440
  }
409
441
  }
410
442
  export {
411
- C as ForceGraph
443
+ S as ForceGraph
412
444
  };
@@ -1 +1 @@
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"})}));
1
+ (function(u,f){typeof exports=="object"&&typeof module<"u"?f(exports,require("force-graph"),require("d3")):typeof define=="function"&&define.amd?define(["exports","force-graph","d3"],f):(u=typeof globalThis<"u"?globalThis:u||self,f(u.ForceGraphLib={},u.ForceGraph,u.d3))})(this,(function(u,f,k){"use strict";function S(c){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(c){for(const o in c)if(o!=="default"){const i=Object.getOwnPropertyDescriptor(c,o);Object.defineProperty(t,o,i.get?i:{enumerable:!0,get:()=>c[o]})}}return t.default=c,Object.freeze(t)}const g=S(k);class v{container;graph;data={nodes:[],links:[]};nodesMap=new Map;linkMap=new Map;options;worker=null;groupHulls=new Map;nodeGroupsCache=null;isFirstRender=!0;autoColorScale=g.scaleOrdinal(g.schemeCategory10);constructor(t,o={nodes:[],links:[]},i={}){this.container=t,this.data=o,this.graph=new f(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",groupFillColor:"rgba(0, 0, 0, 0.05)",groupFillOpacity:1,groupBorderColor:"#666",groupBorderWidth:2,groupBorderOpacity:.3,groupLabelColor:"#333",groupLabelSize:16,groupPadding:20}:{};this.options={...s,...e},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()}),this.graph.onEngineTick(()=>{this.calculateGroupHulls()})}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),l=t.x||0,n=t.y||0;this.drawNodeShape(o,l,n,e,r),o.fillStyle=this.getNodeColor(t),o.fill(),s&&s>0&&(this.drawNodeShape(o,l,n,e,r),o.strokeStyle=this.getNodeBorderColor(t),o.lineWidth=s,o.stroke());const a=this.getNodeLabel(t);if(a&&this.shouldShowLabel(t,i)){const h=typeof this.options.nodeLabelColor=="function"?this.options.nodeLabelColor(t):this.options.nodeLabelColor??"#555",d=(this.options.labelFontSize||14)/i;o.font=`${d}px Arial`,o.fillStyle=h,o.textAlign="center",o.textBaseline="middle",o.fillText(a,t.x||0,(t.y||0)+e+d/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,l=e*.4,n=5;for(let a=0;a<n*2;a++){const h=a%2===0?r:l,p=Math.PI/n*a-Math.PI/2,d=o+h*Math.cos(p),y=i+h*Math.sin(p);a===0?t.moveTo(d,y):t.lineTo(d,y)}t.closePath();break}case"hexagon":{for(let r=0;r<6;r++){const l=Math.PI/3*r-Math.PI/6,n=o+e*Math.cos(l),a=i+e*Math.sin(l);r===0?t.moveTo(n,a):t.lineTo(n,a)}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}calculateGroupHulls(){if(!this.options.showGroups)return;this.groupHulls.clear();const t=this.options.groupPadding||20;this.ensureNodeGroupsCache(),this.nodeGroupsCache.forEach((i,e)=>{if(i.length===0)return;const s=[];let r=1/0,l=0;if(i.forEach(n=>{if(n.x!==void 0&&n.y!==void 0){const h=this.getNodeSize(n)+t;for(let p=0;p<8;p++){const d=p*Math.PI/4;s.push([n.x+h*Math.cos(d),n.y+h*Math.sin(d)])}n.y-h<r&&(r=n.y-h,l=n.x)}}),s.length>=3){const n=g.polygonHull(s);if(n){const a=n.filter(h=>Math.abs(h[1]-r)<t);a.length>0&&(l=a.reduce((h,p)=>h+p[0],0)/a.length),this.groupHulls.set(e,{points:n,labelX:l,labelY:r,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]}}ensureNodeGroupsCache(){this.nodeGroupsCache||(this.nodeGroupsCache=new Map,this.data.nodes.forEach(t=>{const o=this.getNodeGroupId(t);o&&(this.nodeGroupsCache.has(o)||this.nodeGroupsCache.set(o,[]),this.nodeGroupsCache.get(o).push(t))}))}renderGroups(t,o){if(!this.options.showGroups)return;const i=g.line().curve(g.curveCatmullRomClosed).context(t);this.groupHulls.forEach((e,s)=>{const r=this.getGroupFillColor(s),l=this.options.groupFillOpacity??1,n=this.getGroupBorderColor(s),a=this.options.groupBorderWidth??0,h=this.options.groupBorderOpacity??.3;t.save(),t.beginPath(),i(e.points),r&&(t.globalAlpha=l,t.fillStyle=r,t.fill()),a>0&&n&&(t.globalAlpha=h,t.strokeStyle=n,t.lineWidth=a/o,t.setLineDash([]),t.stroke()),t.restore();const p=this.getGroupLabelColor(s),d=(this.options.groupLabelSize||16)/o;t.save(),t.font=`bold ${d}px Arial`,t.fillStyle=p,t.textAlign="center",t.textBaseline="middle";const y=e.labelY-d/2,C=t.measureText(s).width,b=d;t.globalAlpha=.8,t.fillStyle="rgba(255, 255, 255, 0.9)",t.fillRect(e.labelX-C/2-4,y-b/2-2,C+8,b+4),t.globalAlpha=1,t.fillStyle=p,t.fillText(s,e.labelX,y),t.restore()})}getGroupFillColor(t){let o;return typeof this.options.groupFillColor=="function"?o=this.options.groupFillColor(t):o=this.options.groupFillColor,o==="auto"?this.autoColorScale(t):o}getGroupBorderColor(t){let o;return typeof this.options.groupBorderColor=="function"?o=this.options.groupBorderColor(t):o=this.options.groupBorderColor,o==="auto"?this.autoColorScale(t):o||"#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 f(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(),this.nodeGroupsCache=null,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",groupFillColor:"rgba(0, 0, 0, 0.05)",groupFillOpacity:1,groupBorderColor:"#666",groupBorderWidth:2,groupBorderOpacity:.3,groupLabelColor:"#333",groupLabelSize:16,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.fillColor!==void 0&&(this.options.groupFillColor=t.fillColor),t.fillOpacity!==void 0&&(this.options.groupFillOpacity=t.fillOpacity),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.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()}}u.ForceGraph=v,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})}));
package/dist/index.d.ts CHANGED
@@ -39,8 +39,10 @@ export declare class ForceGraph<TNode extends NodeData & {
39
39
  private linkMap;
40
40
  private options;
41
41
  private worker;
42
- private groupBounds;
42
+ private groupHulls;
43
+ private nodeGroupsCache;
43
44
  private isFirstRender;
45
+ private autoColorScale;
44
46
  constructor(container: HTMLElement, initialData?: GraphData<TNode, TLink>, options?: GraphOptions<TNode, TLink>);
45
47
  private initGraph;
46
48
  renderer(): default_2<TNode, TLink>;
@@ -68,17 +70,25 @@ export declare class ForceGraph<TNode extends NodeData & {
68
70
  private getLinkCurvature;
69
71
  private getLinkProperty;
70
72
  /**
71
- * Calculate group boundaries based on node positions
73
+ * Calculate group convex hulls based on node positions
72
74
  */
73
- private calculateGroupBounds;
75
+ private calculateGroupHulls;
74
76
  /**
75
77
  * Get the group ID for a node
76
78
  */
77
79
  private getNodeGroupId;
78
80
  /**
79
- * Render group borders and labels
81
+ * Populate group cache to prevent calculating on every frame
82
+ */
83
+ private ensureNodeGroupsCache;
84
+ /**
85
+ * Render group hulls and labels
80
86
  */
81
87
  private renderGroups;
88
+ /**
89
+ * Get group fill color
90
+ */
91
+ private getGroupFillColor;
82
92
  /**
83
93
  * Get group border color
84
94
  */
@@ -139,12 +149,13 @@ export declare class ForceGraph<TNode extends NodeData & {
139
149
  * Set group visualization options
140
150
  */
141
151
  setGroupOptions(options: {
152
+ fillColor?: string | ((groupId: string) => string);
153
+ fillOpacity?: number;
142
154
  borderColor?: string | ((groupId: string) => string);
143
155
  borderWidth?: number;
144
156
  borderOpacity?: number;
145
157
  labelColor?: string | ((groupId: string) => string);
146
158
  labelSize?: number;
147
- labelThreshold?: number;
148
159
  padding?: number;
149
160
  }): ForceGraph<TNode, TLink>;
150
161
  /**
@@ -196,12 +207,13 @@ export declare interface GraphOptions<N extends NodeData = NodeData, L extends L
196
207
  onGraphUpdated?: () => void;
197
208
  showGroups?: boolean;
198
209
  groupBy?: string | ((node: N) => string | undefined);
210
+ groupFillColor?: string | ((groupId: string) => string);
211
+ groupFillOpacity?: number;
199
212
  groupBorderColor?: string | ((groupId: string) => string);
200
213
  groupBorderWidth?: number;
201
214
  groupBorderOpacity?: number;
202
215
  groupLabelColor?: string | ((groupId: string) => string);
203
216
  groupLabelSize?: number;
204
- groupLabelThreshold?: number;
205
217
  groupPadding?: number;
206
218
  cooldownTime?: number;
207
219
  cooldownTicks?: number;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@logixode/force-graph-lib",
3
3
  "private": false,
4
- "version": "0.5.1",
4
+ "version": "0.6.1",
5
5
  "description": "Force-directed graph visualization library.",
6
6
  "author": "Rohmad Kurniadi",
7
7
  "license": "MIT",