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