@krainovsd/graph 0.14.0-beta.7 → 0.14.0-beta.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/lib/cjs/index.cjs +221 -5
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/esm/module/GraphCanvas/GraphCanvas.js +5 -1
- package/lib/esm/module/GraphCanvas/GraphCanvas.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/utils/svg-context.js +159 -0
- package/lib/esm/module/GraphCanvas/lib/utils/svg-context.js.map +1 -0
- package/lib/esm/module/GraphCanvas/slices/export-svg.js +63 -0
- package/lib/esm/module/GraphCanvas/slices/export-svg.js.map +1 -0
- package/lib/esm/module/GraphCanvas/slices/init-pointer.js +2 -3
- package/lib/esm/module/GraphCanvas/slices/init-pointer.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-resize.js +9 -1
- package/lib/esm/module/GraphCanvas/slices/init-resize.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/package.json +2 -2
package/lib/cjs/index.cjs
CHANGED
|
@@ -2164,7 +2164,7 @@ function initPointer() {
|
|
|
2164
2164
|
this.area.style.cursor = "default";
|
|
2165
2165
|
}
|
|
2166
2166
|
this.animateHighlight(highlightNode ? currentNode : undefined, highlightLink ? currentLink : undefined);
|
|
2167
|
-
if (!this.listeners.
|
|
2167
|
+
if (!this.listeners.onHover)
|
|
2168
2168
|
return;
|
|
2169
2169
|
if (!currentNode && !checkHighlightNode) {
|
|
2170
2170
|
const [pointerX, pointerY] = this.getPointerAreaPosition(event);
|
|
@@ -2184,8 +2184,7 @@ function initPointer() {
|
|
|
2184
2184
|
curve: this.linkSettings.curve,
|
|
2185
2185
|
});
|
|
2186
2186
|
}
|
|
2187
|
-
|
|
2188
|
-
return void this.listeners.onMove.call(this, event, currentNode, currentLink);
|
|
2187
|
+
this.listeners.onHover.call(this, event, currentNode, currentLink);
|
|
2189
2188
|
}
|
|
2190
2189
|
function onWheelClick(event) {
|
|
2191
2190
|
if (this.isDragging ||
|
|
@@ -2328,8 +2327,16 @@ function initResize() {
|
|
|
2328
2327
|
signal: abortController.signal,
|
|
2329
2328
|
});
|
|
2330
2329
|
document.addEventListener("visibilitychange", () => {
|
|
2331
|
-
if (document.hidden)
|
|
2330
|
+
if (document.hidden) {
|
|
2331
|
+
this.highlightController?.abort();
|
|
2332
|
+
this.highlightedNode = null;
|
|
2333
|
+
this.highlightedLink = null;
|
|
2334
|
+
this.highlightedNeighbors = null;
|
|
2335
|
+
this.highlightProgress = 0;
|
|
2336
|
+
this.highlightStart = null;
|
|
2337
|
+
this.highlightPositive = false;
|
|
2332
2338
|
return;
|
|
2339
|
+
}
|
|
2333
2340
|
this.updateSize();
|
|
2334
2341
|
requestAnimationFrame(() => {
|
|
2335
2342
|
this.updateSize();
|
|
@@ -2945,6 +2952,212 @@ function initCollideForce(forceUpdate) {
|
|
|
2945
2952
|
}
|
|
2946
2953
|
}
|
|
2947
2954
|
|
|
2955
|
+
function escapeXml(s) {
|
|
2956
|
+
return s
|
|
2957
|
+
.replace(/&/g, "&")
|
|
2958
|
+
.replace(/</g, "<")
|
|
2959
|
+
.replace(/>/g, ">")
|
|
2960
|
+
.replace(/"/g, """);
|
|
2961
|
+
}
|
|
2962
|
+
class SvgContextAdapter {
|
|
2963
|
+
parts;
|
|
2964
|
+
globalAlpha = 1;
|
|
2965
|
+
fillStyle = "black";
|
|
2966
|
+
strokeStyle = "black";
|
|
2967
|
+
lineWidth = 1;
|
|
2968
|
+
textAlign = "start";
|
|
2969
|
+
_font = "normal normal 400 12px sans-serif";
|
|
2970
|
+
fontFamily = "sans-serif";
|
|
2971
|
+
fontSize = 12;
|
|
2972
|
+
fontWeight = 400;
|
|
2973
|
+
fontStyle = "normal";
|
|
2974
|
+
stateStack = [];
|
|
2975
|
+
currentPath = [];
|
|
2976
|
+
hasPath = false;
|
|
2977
|
+
transformX = 0;
|
|
2978
|
+
transformY = 0;
|
|
2979
|
+
transformK = 1;
|
|
2980
|
+
constructor(parts) {
|
|
2981
|
+
this.parts = parts;
|
|
2982
|
+
}
|
|
2983
|
+
set font(value) {
|
|
2984
|
+
this._font = value;
|
|
2985
|
+
const p = value.trim().split(/\s+/);
|
|
2986
|
+
if (p.length >= 5) {
|
|
2987
|
+
this.fontStyle = p[0];
|
|
2988
|
+
this.fontWeight = parseInt(p[2], 10) || 400;
|
|
2989
|
+
this.fontSize = parseFloat(p[3]) || 12;
|
|
2990
|
+
this.fontFamily = p.slice(4).join(" ");
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
get font() {
|
|
2994
|
+
return this._font;
|
|
2995
|
+
}
|
|
2996
|
+
save() {
|
|
2997
|
+
this.stateStack.push({
|
|
2998
|
+
globalAlpha: this.globalAlpha,
|
|
2999
|
+
fillStyle: this.fillStyle,
|
|
3000
|
+
strokeStyle: this.strokeStyle,
|
|
3001
|
+
lineWidth: this.lineWidth,
|
|
3002
|
+
font: this._font,
|
|
3003
|
+
fontFamily: this.fontFamily,
|
|
3004
|
+
fontSize: this.fontSize,
|
|
3005
|
+
fontWeight: this.fontWeight,
|
|
3006
|
+
fontStyle: this.fontStyle,
|
|
3007
|
+
textAlign: this.textAlign,
|
|
3008
|
+
});
|
|
3009
|
+
}
|
|
3010
|
+
restore() {
|
|
3011
|
+
const state = this.stateStack.pop();
|
|
3012
|
+
if (state) {
|
|
3013
|
+
this.globalAlpha = state.globalAlpha;
|
|
3014
|
+
this.fillStyle = state.fillStyle;
|
|
3015
|
+
this.strokeStyle = state.strokeStyle;
|
|
3016
|
+
this.lineWidth = state.lineWidth;
|
|
3017
|
+
this._font = state.font;
|
|
3018
|
+
this.fontFamily = state.fontFamily;
|
|
3019
|
+
this.fontSize = state.fontSize;
|
|
3020
|
+
this.fontWeight = state.fontWeight;
|
|
3021
|
+
this.fontStyle = state.fontStyle;
|
|
3022
|
+
this.textAlign = state.textAlign;
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
beginPath() {
|
|
3026
|
+
this.currentPath = [];
|
|
3027
|
+
this.hasPath = false;
|
|
3028
|
+
}
|
|
3029
|
+
closePath() {
|
|
3030
|
+
this.currentPath.push("Z");
|
|
3031
|
+
}
|
|
3032
|
+
moveTo(x, y) {
|
|
3033
|
+
this.hasPath = true;
|
|
3034
|
+
this.currentPath.push(`M${x},${y}`);
|
|
3035
|
+
}
|
|
3036
|
+
lineTo(x, y) {
|
|
3037
|
+
this.hasPath = true;
|
|
3038
|
+
this.currentPath.push(`L${x},${y}`);
|
|
3039
|
+
}
|
|
3040
|
+
arc(cx, cy, radius, startAngle, endAngle) {
|
|
3041
|
+
this.hasPath = true;
|
|
3042
|
+
if (endAngle - startAngle >= 2 * Math.PI - 0.01) {
|
|
3043
|
+
const x1 = cx - radius;
|
|
3044
|
+
const x2 = cx + radius;
|
|
3045
|
+
this.currentPath.push(`M${x1},${cy} A${radius},${radius} 0 1,1 ${x2},${cy} A${radius},${radius} 0 1,1 ${x1},${cy}`);
|
|
3046
|
+
}
|
|
3047
|
+
else {
|
|
3048
|
+
const sx = cx + radius * Math.cos(startAngle);
|
|
3049
|
+
const sy = cy + radius * Math.sin(startAngle);
|
|
3050
|
+
const ex = cx + radius * Math.cos(endAngle);
|
|
3051
|
+
const ey = cy + radius * Math.sin(endAngle);
|
|
3052
|
+
this.currentPath.push(`M${sx},${sy} A${radius},${radius} 0 0,1 ${ex},${ey}`);
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
quadraticCurveTo(cpx, cpy, x, y) {
|
|
3056
|
+
this.hasPath = true;
|
|
3057
|
+
this.currentPath.push(`Q${cpx},${cpy} ${x},${y}`);
|
|
3058
|
+
}
|
|
3059
|
+
roundRect(x, y, w, h, radii) {
|
|
3060
|
+
this.hasPath = true;
|
|
3061
|
+
const r = typeof radii === "number" ? radii : (radii[0] ?? 0);
|
|
3062
|
+
if (r < 0.001) {
|
|
3063
|
+
this.currentPath.push(`M${x},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} Z`);
|
|
3064
|
+
}
|
|
3065
|
+
else {
|
|
3066
|
+
this.currentPath.push(`M${x + r},${y} L${x + w - r},${y} A${r},${r} 0 0,1 ${x + w},${y + r} L${x + w},${y + h - r} A${r},${r} 0 0,1 ${x + w - r},${y + h} L${x + r},${y + h} A${r},${r} 0 0,1 ${x},${y + h - r} L${x},${y + r} A${r},${r} 0 0,1 ${x + r},${y} Z`);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
rect(x, y, w, h) {
|
|
3070
|
+
this.hasPath = true;
|
|
3071
|
+
this.currentPath.push(`M${x},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} Z`);
|
|
3072
|
+
}
|
|
3073
|
+
fill() {
|
|
3074
|
+
if (!this.hasPath)
|
|
3075
|
+
return;
|
|
3076
|
+
const d = this.currentPath.join(" ");
|
|
3077
|
+
this.parts.push(`<path d="${d}" fill="${this.fillStyle}" stroke="none" opacity="${this.globalAlpha}" stroke-linejoin="round"/>`);
|
|
3078
|
+
}
|
|
3079
|
+
stroke() {
|
|
3080
|
+
if (!this.hasPath)
|
|
3081
|
+
return;
|
|
3082
|
+
const d = this.currentPath.join(" ");
|
|
3083
|
+
this.parts.push(`<path d="${d}" fill="none" stroke="${this.strokeStyle}" stroke-width="${this.lineWidth}" opacity="${this.globalAlpha}" stroke-linecap="round" stroke-linejoin="round"/>`);
|
|
3084
|
+
}
|
|
3085
|
+
fillText(text, x, y) {
|
|
3086
|
+
const anchor = this.textAlign === "center" ? "middle" : this.textAlign === "right" ? "end" : "start";
|
|
3087
|
+
this.parts.push(`<text x="${x}" y="${y}" font-family="${this.fontFamily}" font-size="${this.fontSize}" font-weight="${this.fontWeight}" font-style="${this.fontStyle}" fill="${this.fillStyle}" text-anchor="${anchor}" opacity="${this.globalAlpha}">${escapeXml(text)}</text>`);
|
|
3088
|
+
}
|
|
3089
|
+
fillRect(x, y, w, h) {
|
|
3090
|
+
this.parts.push(`<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="${this.fillStyle}" opacity="${this.globalAlpha}"/>`);
|
|
3091
|
+
}
|
|
3092
|
+
strokeRect(x, y, w, h) {
|
|
3093
|
+
this.parts.push(`<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="none" stroke="${this.strokeStyle}" stroke-width="${this.lineWidth}" opacity="${this.globalAlpha}"/>`);
|
|
3094
|
+
}
|
|
3095
|
+
clearRect() { }
|
|
3096
|
+
translate(x, y) {
|
|
3097
|
+
this.transformX = x;
|
|
3098
|
+
this.transformY = y;
|
|
3099
|
+
}
|
|
3100
|
+
scale(k) {
|
|
3101
|
+
this.transformK = k;
|
|
3102
|
+
}
|
|
3103
|
+
closeTransform() {
|
|
3104
|
+
this.parts.unshift(`<g transform="translate(${this.transformX},${this.transformY}) scale(${this.transformK})">`);
|
|
3105
|
+
this.parts.push("</g>");
|
|
3106
|
+
}
|
|
3107
|
+
setLineDash() { }
|
|
3108
|
+
clip() { }
|
|
3109
|
+
drawImage() { }
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
function exportToSvgSlice(filename = "graph.svg", fit = false) {
|
|
3113
|
+
this.tick();
|
|
3114
|
+
let svgWidth = this.width;
|
|
3115
|
+
let svgHeight = this.height;
|
|
3116
|
+
const svgParts = [];
|
|
3117
|
+
const adapter = new SvgContextAdapter(svgParts);
|
|
3118
|
+
const clone = Object.assign(Object.create(Object.getPrototypeOf(this)), this);
|
|
3119
|
+
clone.context = adapter;
|
|
3120
|
+
clone.isSelecting = false;
|
|
3121
|
+
clone.listeners = {};
|
|
3122
|
+
clone.nodes = jsHelpers.cloneDeep(this.nodes);
|
|
3123
|
+
clone.links = jsHelpers.cloneDeep(this.links);
|
|
3124
|
+
clone.particles = jsHelpers.cloneDeep(this.particles);
|
|
3125
|
+
if (fit) {
|
|
3126
|
+
const bounds = computeGraphBounds(clone.nodes);
|
|
3127
|
+
if (bounds) {
|
|
3128
|
+
const pad = 50;
|
|
3129
|
+
const graphWidth = bounds.maxX - bounds.minX;
|
|
3130
|
+
const graphHeight = bounds.maxY - bounds.minY;
|
|
3131
|
+
if (graphWidth > 0 && graphHeight > 0) {
|
|
3132
|
+
svgWidth = graphWidth + pad * 2;
|
|
3133
|
+
svgHeight = graphHeight + pad * 2;
|
|
3134
|
+
const graphCenterX = bounds.minX + graphWidth / 2;
|
|
3135
|
+
const graphCenterY = bounds.minY + graphHeight / 2;
|
|
3136
|
+
clone.areaTransform = d3Zoom.zoomIdentity
|
|
3137
|
+
.translate(svgWidth / 2, svgHeight / 2)
|
|
3138
|
+
.translate(-graphCenterX, -graphCenterY);
|
|
3139
|
+
clone.width = svgWidth;
|
|
3140
|
+
clone.height = svgHeight;
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
const draw = initDraw.call(clone);
|
|
3145
|
+
draw();
|
|
3146
|
+
adapter.closeTransform();
|
|
3147
|
+
const svg = [
|
|
3148
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="${svgWidth}" height="${svgHeight}">`,
|
|
3149
|
+
...svgParts,
|
|
3150
|
+
"</svg>",
|
|
3151
|
+
].join("\n");
|
|
3152
|
+
const blob = new Blob([svg], { type: "image/svg+xml;charset=utf-8" });
|
|
3153
|
+
const url = URL.createObjectURL(blob);
|
|
3154
|
+
const a = document.createElement("a");
|
|
3155
|
+
a.href = url;
|
|
3156
|
+
a.download = filename;
|
|
3157
|
+
a.click();
|
|
3158
|
+
URL.revokeObjectURL(url);
|
|
3159
|
+
}
|
|
3160
|
+
|
|
2948
3161
|
class GraphCanvas {
|
|
2949
3162
|
/** initial data */
|
|
2950
3163
|
nodes;
|
|
@@ -3074,6 +3287,9 @@ class GraphCanvas {
|
|
|
3074
3287
|
}
|
|
3075
3288
|
this.animateZoom(area, target, this.areaTransform, duration);
|
|
3076
3289
|
};
|
|
3290
|
+
exportToSvg = (filename = "graph.svg", fit = false) => {
|
|
3291
|
+
exportToSvgSlice.call(this, filename, fit);
|
|
3292
|
+
};
|
|
3077
3293
|
changeData = (options, alpha = 0.5, clearCache = true, precompute = false) => {
|
|
3078
3294
|
if (options.links != undefined)
|
|
3079
3295
|
this.links = options.links;
|
|
@@ -3376,7 +3592,7 @@ class GraphCanvas {
|
|
|
3376
3592
|
if (controller.signal.aborted)
|
|
3377
3593
|
return;
|
|
3378
3594
|
const elapsed = performance.now() - startTime;
|
|
3379
|
-
const t = Math.min(elapsed / duration, 1);
|
|
3595
|
+
const t = duration === 0 ? 1 : Math.min(elapsed / duration, 1);
|
|
3380
3596
|
const current = startProgress + delta * t;
|
|
3381
3597
|
const eased = current < 0.5 ? 4 * current * current * current : 1 - (-2 * current + 2) ** 3 / 2;
|
|
3382
3598
|
this.highlightProgress = eased;
|