@ngroznykh/papirus 0.3.0 → 0.3.1

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/papirus.js CHANGED
@@ -8104,6 +8104,7 @@ class SvgExporter {
8104
8104
  const padding = options.padding ?? 20;
8105
8105
  const includeBackground = options.includeBackground ?? true;
8106
8106
  const backgroundColor = options.backgroundColor ?? "#ffffff";
8107
+ const edgeLabelOffset = options.edgeLabelOffset ?? 0;
8107
8108
  const bounds = getContentBounds({
8108
8109
  nodes: this.renderer.nodes.values(),
8109
8110
  edges: this.renderer.edges.values(),
@@ -8123,10 +8124,17 @@ class SvgExporter {
8123
8124
  this.renderer.nodes.values()
8124
8125
  );
8125
8126
  const parts = [];
8127
+ const markerDefs = /* @__PURE__ */ new Map();
8128
+ const renderedEdges = [];
8126
8129
  parts.push(
8127
8130
  `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">`
8128
8131
  );
8129
- parts.push(this.arrowDefs());
8132
+ for (const edge of this.renderer.edges.values()) {
8133
+ if (edge.visible) {
8134
+ renderedEdges.push(this.renderEdge(edge, markerDefs, edgeLabelOffset));
8135
+ }
8136
+ }
8137
+ parts.push(this.buildDefs(markerDefs));
8130
8138
  if (includeBackground) {
8131
8139
  parts.push(`<rect width="100%" height="100%" fill="${backgroundColor}"/>`);
8132
8140
  }
@@ -8136,11 +8144,7 @@ class SvgExporter {
8136
8144
  parts.push(this.renderGroup(group));
8137
8145
  }
8138
8146
  }
8139
- for (const edge of this.renderer.edges.values()) {
8140
- if (edge.visible) {
8141
- parts.push(this.renderEdge(edge));
8142
- }
8143
- }
8147
+ parts.push(...renderedEdges);
8144
8148
  for (const node of this.renderer.nodes.values()) {
8145
8149
  if (node.visible) {
8146
8150
  parts.push(this.renderNode(node));
@@ -8218,7 +8222,7 @@ class SvgExporter {
8218
8222
  label
8219
8223
  ].join("");
8220
8224
  }
8221
- renderEdge(edge) {
8225
+ renderEdge(edge, markerDefs, edgeLabelOffset) {
8222
8226
  const path = edge.path;
8223
8227
  if (path.length < 2) {
8224
8228
  return "";
@@ -8231,11 +8235,59 @@ class SvgExporter {
8231
8235
  const dash = dashValues ? ` stroke-dasharray="${dashValues.join(" ")}"` : "";
8232
8236
  const dashOffset = style.lineDashOffset !== void 0 ? ` stroke-dashoffset="${style.lineDashOffset}"` : "";
8233
8237
  const d = this.buildPath(edge);
8234
- const markerEnd = edge.arrowType === "none" ? "" : ` marker-end="url(#arrow)"`;
8235
- const markerStart = edge.arrowType === "double" ? ` marker-start="url(#arrow)"` : "";
8236
- const label = edge.label ? this.renderTextLabel(edge.label.text, this.getPathMidpoint(edge), edge.label.style) : "";
8238
+ const startMarkerConfig = this.resolveMarkerConfig(edge, "start");
8239
+ const endMarkerConfig = this.resolveMarkerConfig(edge, "end");
8240
+ const markerStart = startMarkerConfig ? ` marker-start="url(#${this.ensureMarkerDef(markerDefs, startMarkerConfig, "start", stroke)})"` : "";
8241
+ const markerEnd = endMarkerConfig ? ` marker-end="url(#${this.ensureMarkerDef(markerDefs, endMarkerConfig, "end", stroke)})"` : "";
8242
+ const label = edge.label ? this.renderTextLabel(edge.label.text, this.getEdgeLabelPoint(edge, edgeLabelOffset), edge.label.style) : "";
8237
8243
  return `<path d="${d}" fill="none" stroke="${stroke}" stroke-width="${strokeWidth}" opacity="${opacity}" color="${stroke}"${dash}${dashOffset}${markerStart}${markerEnd}/>${label}`;
8238
8244
  }
8245
+ resolveMarkerConfig(edge, side) {
8246
+ const marker = side === "start" ? edge.startMarker : edge.endMarker;
8247
+ if (marker && marker.type !== "none") {
8248
+ return marker;
8249
+ }
8250
+ if (side === "end") {
8251
+ return edge.arrowType === "none" ? null : { type: "arrow" };
8252
+ }
8253
+ return edge.arrowType === "double" ? { type: "arrow" } : null;
8254
+ }
8255
+ ensureMarkerDef(markerDefs, marker, side, stroke) {
8256
+ const size = marker.size ?? 12;
8257
+ const markerStroke = marker.strokeColor ?? stroke;
8258
+ const markerFill = marker.fillColor ?? markerStroke;
8259
+ const fillOpacity = marker.fillOpacity ?? 1;
8260
+ const markerId = this.buildMarkerId(side, marker.type, size, markerFill, markerStroke, fillOpacity);
8261
+ if (!markerDefs.has(markerId)) {
8262
+ const refX = marker.type === "circle" ? 5 : 10;
8263
+ markerDefs.set(
8264
+ markerId,
8265
+ [
8266
+ `<marker id="${markerId}" viewBox="0 0 10 10" refX="${refX}" refY="5" markerWidth="${size}" markerHeight="${size}" orient="auto-start-reverse" markerUnits="userSpaceOnUse">`,
8267
+ this.renderMarkerShape(marker, markerFill, markerStroke, fillOpacity),
8268
+ `</marker>`
8269
+ ].join("")
8270
+ );
8271
+ }
8272
+ return markerId;
8273
+ }
8274
+ buildMarkerId(side, type, size, fill, stroke, fillOpacity) {
8275
+ const token = `${side}-${type}-${size}-${fill}-${stroke}-${fillOpacity}`.toLowerCase().replace(/[^a-z0-9_-]+/g, "-");
8276
+ return `marker-${token}`;
8277
+ }
8278
+ renderMarkerShape(marker, fill, stroke, fillOpacity) {
8279
+ switch (marker.type) {
8280
+ case "open":
8281
+ return `<path d="M 10 0 L 0 5 L 10 10" fill="none" stroke="${stroke}" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>`;
8282
+ case "diamond":
8283
+ return `<path d="M 10 5 L 5 0 L 0 5 L 5 10 z" fill="${fill}" fill-opacity="${fillOpacity}" stroke="${stroke}" stroke-width="1"/>`;
8284
+ case "circle":
8285
+ return `<circle cx="5" cy="5" r="4" fill="${fill}" fill-opacity="${fillOpacity}" stroke="${stroke}" stroke-width="1"/>`;
8286
+ case "arrow":
8287
+ default:
8288
+ return `<path d="M 0 0 L 10 5 L 0 10 z" fill="${fill}" fill-opacity="${fillOpacity}" stroke="${stroke}" stroke-width="1"/>`;
8289
+ }
8290
+ }
8239
8291
  buildPath(edge) {
8240
8292
  const path = edge.path;
8241
8293
  if (edge.type === "bezier" && path.length >= 4) {
@@ -8284,6 +8336,13 @@ class SvgExporter {
8284
8336
  }
8285
8337
  return path[0];
8286
8338
  }
8339
+ getEdgeLabelPoint(edge, edgeLabelOffset) {
8340
+ const midpoint = this.getPathMidpoint(edge);
8341
+ return {
8342
+ x: midpoint.x,
8343
+ y: midpoint.y + edgeLabelOffset
8344
+ };
8345
+ }
8287
8346
  renderTextLabel(text, point, style = {}) {
8288
8347
  const fill = style.color ?? "#000000";
8289
8348
  const fontSize = style.fontSize ?? 14;
@@ -8295,14 +8354,11 @@ class SvgExporter {
8295
8354
  text
8296
8355
  )}</text>`;
8297
8356
  }
8298
- arrowDefs() {
8299
- return [
8300
- "<defs>",
8301
- '<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">',
8302
- '<path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor"/>',
8303
- "</marker>",
8304
- "</defs>"
8305
- ].join("");
8357
+ buildDefs(markerDefs) {
8358
+ if (markerDefs.size === 0) {
8359
+ return "<defs></defs>";
8360
+ }
8361
+ return `<defs>${Array.from(markerDefs.values()).join("")}</defs>`;
8306
8362
  }
8307
8363
  createEmptySvg(width, height, backgroundColor, includeBackground) {
8308
8364
  return [