@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 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.onMove)
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
- if (!currentNode)
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, "&lt;")
2959
+ .replace(/>/g, "&gt;")
2960
+ .replace(/"/g, "&quot;");
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;