@logixode/force-graph-lib 0.1.8 → 0.1.10
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.
- package/dist/force-graph-lib.js +12 -7
- package/dist/force-graph-lib.umd.cjs +1 -1
- package/dist/index.d.ts +64 -40
- package/package.json +1 -1
package/dist/force-graph-lib.js
CHANGED
|
@@ -9,6 +9,7 @@ class C {
|
|
|
9
9
|
options;
|
|
10
10
|
worker = null;
|
|
11
11
|
groupBounds = /* @__PURE__ */ new Map();
|
|
12
|
+
isFirstRender = !0;
|
|
12
13
|
constructor(t, i = { nodes: [], links: [] }, o = {}) {
|
|
13
14
|
this.container = t, this.data = i, this.graph = new f(this.container), i.nodes.forEach((r) => {
|
|
14
15
|
this.nodesMap.set(r.id.toString(), r);
|
|
@@ -34,7 +35,9 @@ class C {
|
|
|
34
35
|
}, this.initGraph();
|
|
35
36
|
}
|
|
36
37
|
initGraph() {
|
|
37
|
-
this.applyOptions(), this.render(), this.graphData(this.data), this.graph.onEngineStop(() =>
|
|
38
|
+
this.applyOptions(), this.render(), this.graphData(this.data), this.graph.onEngineStop(() => {
|
|
39
|
+
this.isFirstRender ? (this.options.onRenderComplete ? this.options.onRenderComplete() : this.graph.zoomToFit(400), this.isFirstRender = !1) : this.options.onGraphUpdated && this.options.onGraphUpdated();
|
|
40
|
+
});
|
|
38
41
|
}
|
|
39
42
|
renderer() {
|
|
40
43
|
return this.graph;
|
|
@@ -71,10 +74,10 @@ class C {
|
|
|
71
74
|
}), this.applyLinkOptions();
|
|
72
75
|
}
|
|
73
76
|
getNodeSize(t) {
|
|
74
|
-
return typeof this.options.nodeSize == "function" ? this.options.nodeSize(t) || t
|
|
77
|
+
return typeof this.options.nodeSize == "function" ? this.options.nodeSize(t) || t?.marker?.radius : this.options.nodeSize || t?.marker?.radius || 1;
|
|
75
78
|
}
|
|
76
79
|
getNodeLabel(t) {
|
|
77
|
-
return typeof this.options.nodeLabel == "function" ? this.options.nodeLabel(t) : t
|
|
80
|
+
return typeof this.options.nodeLabel == "function" ? this.options.nodeLabel(t) : t?.label || t.id;
|
|
78
81
|
}
|
|
79
82
|
/**
|
|
80
83
|
* Check if label should be shown based on threshold logic
|
|
@@ -104,13 +107,15 @@ class C {
|
|
|
104
107
|
const i = this.options.nodeColor(t);
|
|
105
108
|
if (i) return i;
|
|
106
109
|
}
|
|
107
|
-
return t
|
|
110
|
+
return t?.color ?? "";
|
|
108
111
|
}
|
|
109
112
|
getNodeBorderColor(t) {
|
|
110
113
|
return typeof this.options.nodeBorderColor == "function" ? this.options.nodeBorderColor(t) : this.options.nodeBorderColor || "#333";
|
|
111
114
|
}
|
|
112
115
|
applyLinkOptions() {
|
|
113
|
-
this.options.linkWidth !== void 0 && this.graph.linkWidth(
|
|
116
|
+
this.options.linkWidth !== void 0 && this.graph.linkWidth(
|
|
117
|
+
(t) => this.getLinkProperty(this.options.linkWidth, t) ?? 1
|
|
118
|
+
), this.options.linkCurvature !== void 0 && this.graph.linkCurvature(this.getLinkCurvature.bind(this)), this.options.linkDirectionalParticles && this.graph.linkDirectionalParticles(
|
|
114
119
|
(t) => this.getLinkProperty(this.options.linkDirectionalParticles, t) ?? 0
|
|
115
120
|
), this.options.linkDirectionalParticleSpeed !== void 0 && this.graph.linkDirectionalParticleSpeed(
|
|
116
121
|
(t) => this.getLinkProperty(this.options.linkDirectionalParticleSpeed, t) ?? 0
|
|
@@ -239,7 +244,7 @@ class C {
|
|
|
239
244
|
const i = t.toString();
|
|
240
245
|
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) => {
|
|
241
246
|
const e = typeof o.source == "object" ? o.source.id : o.source, s = typeof o.target == "object" ? o.target.id : o.target;
|
|
242
|
-
return e
|
|
247
|
+
return e?.toString() !== i && s?.toString() !== i;
|
|
243
248
|
}), this.graph.cooldownTime(this.getCooldownTime()), this.refreshGraph(), !0) : !1;
|
|
244
249
|
}
|
|
245
250
|
async addData(t) {
|
|
@@ -286,7 +291,7 @@ class C {
|
|
|
286
291
|
return this.data.links;
|
|
287
292
|
}
|
|
288
293
|
createLinkKey(t, i) {
|
|
289
|
-
const o = typeof t == "object" ? t.id : t, e = typeof i == "object" ? i.id : i;
|
|
294
|
+
const o = t === void 0 ? "undefined" : typeof t == "object" ? t.id : t, e = i === void 0 ? "undefined" : typeof i == "object" ? i.id : i;
|
|
290
295
|
return `${o}-${e}`;
|
|
291
296
|
}
|
|
292
297
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(a,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):(a=typeof globalThis<"u"?globalThis:a||self,l(a.ForceGraphLib={},a.ForceGraph,a.d3))})(this,(function(a,l,b){"use strict";function k(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=k(b);class C{container;graph;data={nodes:[],links:[]};nodesMap=new Map;linkMap=new Map;options;worker=null;groupBounds=new Map;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(()=>this.graph.zoomToFit(400))}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)),typeof this.options.nodeClickHandler=="function"&&this.graph.onNodeClick(t=>{this.options.nodeClickHandler(t)})}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.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 h=this.getNodeSize(n);s=Math.min(s,n.x-h),r=Math.min(r,n.y-h),p=Math.max(p,n.x+h),u=Math.max(u,n.y+h)}}),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),h=(this.options.groupLabelSize||16)/i;t.save(),t.font=`bold ${h}px Arial`,t.fillStyle=n,t.textAlign="center",t.textBaseline="middle";const g=(o.minX+o.maxX)/2,f=o.minY-h/2,c=t.measureText(e).width,y=h;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(){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=typeof t=="object"?t.id:t,e=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()}}a.ForceGraph=C,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})}));
|
|
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,b){"use strict";function k(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=k(b);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(()=>{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)),typeof this.options.nodeClickHandler=="function"&&this.graph.onNodeClick(t=>{this.options.nodeClickHandler(t)})}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.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(){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"})}));
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,29 @@ declare interface ForceFn<N extends NodeObject = NodeObject> {
|
|
|
9
9
|
[key: string]: any;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* ForceGraph with flexible generic types.
|
|
14
|
+
*
|
|
15
|
+
* - `TNode`: your node shape; must include an `id` (`string | number`).
|
|
16
|
+
* - `TLink`: your link shape; by default `LinkObject<TNode>`.
|
|
17
|
+
*
|
|
18
|
+
* Usage examples:
|
|
19
|
+
*
|
|
20
|
+
* // 1) Use defaults (compatible with NodeData/LinkObject)
|
|
21
|
+
* const graph = new ForceGraph(container, defaultData)
|
|
22
|
+
*
|
|
23
|
+
* // 2) Provide custom shapes
|
|
24
|
+
* type MyNode = NodeObject & { id: string; color?: string; size?: number }
|
|
25
|
+
* type MyLink = LinkObject<MyNode> & { weight?: number }
|
|
26
|
+
* const graph = new ForceGraph<MyNode, MyLink>(container, myData, {
|
|
27
|
+
* nodeSize: (n) => n.size ?? 2,
|
|
28
|
+
* nodeColor: (n) => n.color ?? '#999',
|
|
29
|
+
* linkWidth: (l) => (l.weight ?? 1) * 2,
|
|
30
|
+
* })
|
|
31
|
+
*/
|
|
32
|
+
export declare class ForceGraph<TNode extends NodeData & {
|
|
33
|
+
id: string | number;
|
|
34
|
+
} = NodeData, TLink extends LinkData<TNode> = LinkData<TNode>> {
|
|
13
35
|
private container;
|
|
14
36
|
private graph;
|
|
15
37
|
private data;
|
|
@@ -18,16 +40,17 @@ export declare class ForceGraph {
|
|
|
18
40
|
private options;
|
|
19
41
|
private worker;
|
|
20
42
|
private groupBounds;
|
|
21
|
-
|
|
43
|
+
private isFirstRender;
|
|
44
|
+
constructor(container: HTMLElement, initialData?: GraphData<TNode, TLink>, options?: GraphOptions<TNode, TLink>);
|
|
22
45
|
private initGraph;
|
|
23
|
-
renderer(): default_2<
|
|
46
|
+
renderer(): default_2<TNode, TLink>;
|
|
24
47
|
focusPosition(nodeData?: {
|
|
25
48
|
id?: string;
|
|
26
49
|
x?: number;
|
|
27
50
|
y?: number;
|
|
28
51
|
}): void;
|
|
29
52
|
render(): void;
|
|
30
|
-
force(key: ForceType, func: ForceFn<
|
|
53
|
+
force(key: ForceType, func: ForceFn<TNode>): default_2<TNode, TLink>;
|
|
31
54
|
private applyOptions;
|
|
32
55
|
private getNodeSize;
|
|
33
56
|
private getNodeLabel;
|
|
@@ -36,7 +59,7 @@ export declare class ForceGraph {
|
|
|
36
59
|
* The threshold determines when labels become too big relative to nodes
|
|
37
60
|
*/
|
|
38
61
|
private shouldShowLabel;
|
|
39
|
-
updateData(data: GraphData): void;
|
|
62
|
+
updateData(data: GraphData<TNode, TLink>): void;
|
|
40
63
|
private getNodeColor;
|
|
41
64
|
private getNodeBorderColor;
|
|
42
65
|
private applyLinkOptions;
|
|
@@ -62,7 +85,7 @@ export declare class ForceGraph {
|
|
|
62
85
|
* Get group label color
|
|
63
86
|
*/
|
|
64
87
|
private getGroupLabelColor;
|
|
65
|
-
getNodeById(id: string | number):
|
|
88
|
+
getNodeById(id: string | number): TNode | undefined;
|
|
66
89
|
hasNode(id: string | number): boolean;
|
|
67
90
|
/**
|
|
68
91
|
* Calculate dynamic cooldown time based on node count
|
|
@@ -79,11 +102,11 @@ export declare class ForceGraph {
|
|
|
79
102
|
links: number;
|
|
80
103
|
};
|
|
81
104
|
getAllNodeIds(): string[];
|
|
82
|
-
updateNode(id: string | number, updates: Partial<
|
|
105
|
+
updateNode(id: string | number, updates: Partial<TNode>): boolean;
|
|
83
106
|
removeNode(id: string | number): boolean;
|
|
84
|
-
addData(newData: GraphData): Promise<void>;
|
|
107
|
+
addData(newData: GraphData<TNode, TLink>): Promise<void>;
|
|
85
108
|
setLabelThreshold(threshold: number): void;
|
|
86
|
-
setOptions(options: Partial<GraphOptions
|
|
109
|
+
setOptions(options: Partial<GraphOptions<TNode, TLink>>): void;
|
|
87
110
|
/**
|
|
88
111
|
* Lightweight refresh - only updates graph data
|
|
89
112
|
*/
|
|
@@ -93,23 +116,23 @@ export declare class ForceGraph {
|
|
|
93
116
|
*/
|
|
94
117
|
reinitialize(): void;
|
|
95
118
|
reset(): void;
|
|
96
|
-
getData(): GraphData
|
|
97
|
-
getNodesData(): GraphData['nodes'];
|
|
98
|
-
getLinksData(): GraphData['links'];
|
|
119
|
+
getData(): GraphData<TNode, TLink>;
|
|
120
|
+
getNodesData(): GraphData<TNode, TLink>['nodes'];
|
|
121
|
+
getLinksData(): GraphData<TNode, TLink>['links'];
|
|
99
122
|
private createLinkKey;
|
|
100
123
|
/**
|
|
101
124
|
* Set graph data (chainable method)
|
|
102
125
|
* @param data - Graph data to set
|
|
103
126
|
*/
|
|
104
|
-
graphData(data: GraphData): ForceGraph
|
|
127
|
+
graphData(data: GraphData<TNode, TLink>): ForceGraph<TNode, TLink>;
|
|
105
128
|
/**
|
|
106
129
|
* Enable or disable group visualization
|
|
107
130
|
*/
|
|
108
|
-
showGroups(show: boolean): ForceGraph
|
|
131
|
+
showGroups(show: boolean): ForceGraph<TNode, TLink>;
|
|
109
132
|
/**
|
|
110
133
|
* Set the property to group nodes by
|
|
111
134
|
*/
|
|
112
|
-
setGroupBy(groupBy: string | ((node:
|
|
135
|
+
setGroupBy(groupBy: string | ((node: TNode) => string | undefined)): ForceGraph<TNode, TLink>;
|
|
113
136
|
/**
|
|
114
137
|
* Set group visualization options
|
|
115
138
|
*/
|
|
@@ -121,7 +144,7 @@ export declare class ForceGraph {
|
|
|
121
144
|
labelSize?: number;
|
|
122
145
|
labelThreshold?: number;
|
|
123
146
|
padding?: number;
|
|
124
|
-
}): ForceGraph
|
|
147
|
+
}): ForceGraph<TNode, TLink>;
|
|
125
148
|
/**
|
|
126
149
|
* Get all available groups
|
|
127
150
|
*/
|
|
@@ -129,46 +152,47 @@ export declare class ForceGraph {
|
|
|
129
152
|
/**
|
|
130
153
|
* Get nodes in a specific group
|
|
131
154
|
*/
|
|
132
|
-
getNodesInGroup(groupId: string):
|
|
155
|
+
getNodesInGroup(groupId: string): TNode[];
|
|
133
156
|
/**
|
|
134
157
|
* Get current options
|
|
135
158
|
*/
|
|
136
|
-
getOptions(): GraphOptions
|
|
159
|
+
getOptions(): GraphOptions<TNode, TLink>;
|
|
137
160
|
destroy(): void;
|
|
138
161
|
}
|
|
139
162
|
|
|
140
163
|
declare type ForceType = 'link' | 'charge' | 'center' | 'cluster' | string;
|
|
141
164
|
|
|
142
|
-
export declare interface GraphData extends
|
|
165
|
+
export declare interface GraphData<N extends NodeData = NodeData, L extends LinkData<N> = LinkData<N>> extends GraphData_2<N, L> {
|
|
143
166
|
}
|
|
144
167
|
|
|
145
|
-
export declare interface GraphOptions {
|
|
168
|
+
export declare interface GraphOptions<N extends NodeData = NodeData, L extends LinkData<N> = LinkData<N>> {
|
|
146
169
|
height?: number;
|
|
147
170
|
width?: number;
|
|
148
171
|
labelThreshold?: number;
|
|
149
|
-
nodeSize?: number | ((node:
|
|
150
|
-
linkWidth?: number | ((link:
|
|
151
|
-
nodeLabel?: string | ((node:
|
|
152
|
-
nodeLabelColor?: string | ((node:
|
|
172
|
+
nodeSize?: number | ((node: N) => number);
|
|
173
|
+
linkWidth?: number | ((link: L) => number);
|
|
174
|
+
nodeLabel?: string | ((node: N) => string);
|
|
175
|
+
nodeLabelColor?: string | ((node: N) => string);
|
|
153
176
|
labelFontSize?: number;
|
|
154
|
-
nodeColor?: string | ((node:
|
|
155
|
-
nodeBorderColor?: string | ((node:
|
|
156
|
-
nodeBorderWidth?: number | ((node:
|
|
177
|
+
nodeColor?: string | ((node: N) => string);
|
|
178
|
+
nodeBorderColor?: string | ((node: N) => string);
|
|
179
|
+
nodeBorderWidth?: number | ((node: N) => number);
|
|
157
180
|
nodeGap?: number;
|
|
158
|
-
linkLabel?: string | ((link:
|
|
159
|
-
nodeIcon?: string | ((node:
|
|
160
|
-
cluster?: (node: NodeData) => boolean | undefined | null;
|
|
181
|
+
linkLabel?: string | ((link: L) => string);
|
|
182
|
+
nodeIcon?: string | ((node: N) => string);
|
|
161
183
|
loading?: boolean;
|
|
162
184
|
pointerInteraction?: boolean;
|
|
163
185
|
keepDragPosition?: boolean;
|
|
164
|
-
nodeClickHandler?: (node:
|
|
165
|
-
linkCurvature?: number | string | ((link:
|
|
166
|
-
linkDirectionalParticles?: number | ((link:
|
|
167
|
-
linkDirectionalParticleSpeed?: number | ((link:
|
|
168
|
-
linkDirectionalParticleWidth?: number | ((link:
|
|
169
|
-
linkDirectionalParticleColor?: string | ((link:
|
|
186
|
+
nodeClickHandler?: (node: N) => void;
|
|
187
|
+
linkCurvature?: number | string | ((link: L) => number);
|
|
188
|
+
linkDirectionalParticles?: number | ((link: L) => number);
|
|
189
|
+
linkDirectionalParticleSpeed?: number | ((link: L) => number);
|
|
190
|
+
linkDirectionalParticleWidth?: number | ((link: L) => number);
|
|
191
|
+
linkDirectionalParticleColor?: string | ((link: L) => string);
|
|
192
|
+
onRenderComplete?: () => void;
|
|
193
|
+
onGraphUpdated?: () => void;
|
|
170
194
|
showGroups?: boolean;
|
|
171
|
-
groupBy?: string | ((node:
|
|
195
|
+
groupBy?: string | ((node: N) => string | undefined);
|
|
172
196
|
groupBorderColor?: string | ((groupId: string) => string);
|
|
173
197
|
groupBorderWidth?: number;
|
|
174
198
|
groupBorderOpacity?: number;
|
|
@@ -178,9 +202,9 @@ export declare interface GraphOptions {
|
|
|
178
202
|
groupPadding?: number;
|
|
179
203
|
}
|
|
180
204
|
|
|
181
|
-
export declare interface LinkData extends LinkObject<
|
|
182
|
-
source: string | number |
|
|
183
|
-
target: string | number |
|
|
205
|
+
export declare interface LinkData<N extends NodeData = NodeData> extends LinkObject<N> {
|
|
206
|
+
source: string | number | N;
|
|
207
|
+
target: string | number | N;
|
|
184
208
|
weight?: number;
|
|
185
209
|
color?: string;
|
|
186
210
|
curvature?: number;
|