@krainovsd/graph 0.14.0-beta.8 → 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
@@ -2952,6 +2952,212 @@ function initCollideForce(forceUpdate) {
2952
2952
  }
2953
2953
  }
2954
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
+
2955
3161
  class GraphCanvas {
2956
3162
  /** initial data */
2957
3163
  nodes;
@@ -3081,6 +3287,9 @@ class GraphCanvas {
3081
3287
  }
3082
3288
  this.animateZoom(area, target, this.areaTransform, duration);
3083
3289
  };
3290
+ exportToSvg = (filename = "graph.svg", fit = false) => {
3291
+ exportToSvgSlice.call(this, filename, fit);
3292
+ };
3084
3293
  changeData = (options, alpha = 0.5, clearCache = true, precompute = false) => {
3085
3294
  if (options.links != undefined)
3086
3295
  this.links = options.links;