@stocksharp/diagram 0.3.0 → 0.4.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.
@@ -143,11 +143,12 @@ export class NodeModel {
143
143
  }
144
144
  }
145
145
  export class LinkModel {
146
- constructor(from, fromPort, to, toPort, id = '', metadata) {
146
+ constructor(from, fromPort, to, toPort, id = '', metadata, style = 'solid') {
147
147
  this.from = from;
148
148
  this.fromPort = fromPort;
149
149
  this.to = to;
150
150
  this.toPort = toPort;
151
+ this.style = style;
151
152
  this.id = id;
152
153
  this.metadata = copyJsonObject(metadata);
153
154
  }
@@ -159,6 +160,7 @@ export class LinkModel {
159
160
  fromPort: this.fromPort,
160
161
  to: this.to,
161
162
  toPort: this.toPort,
163
+ style: this.style,
162
164
  metadata: copyJsonObject(this.metadata),
163
165
  };
164
166
  }
@@ -219,6 +221,7 @@ export class Diagram {
219
221
  constructor(opts) {
220
222
  this.nodes = [];
221
223
  this.links = [];
224
+ this.zones = [];
222
225
  this.idSeq = 1;
223
226
  this.linkSeq = 1;
224
227
  this.documentMetadata = {};
@@ -565,7 +568,7 @@ export class Diagram {
565
568
  if (!validation.allowed)
566
569
  return false;
567
570
  }
568
- const link = new LinkModel(init.from, init.fromPort, init.to, init.toPort, init.id ?? this.nextLinkId(), init.metadata);
571
+ const link = new LinkModel(init.from, init.fromPort, init.to, init.toPort, init.id ?? this.nextLinkId(), init.metadata, init.style ?? 'solid');
569
572
  if (this.links.some((l) => l.id === link.id))
570
573
  return false;
571
574
  this.links.splice(clamp(Math.trunc(index), 0, this.links.length), 0, link);
@@ -975,7 +978,7 @@ export class Diagram {
975
978
  if (this.links.some((existing) => existing.id === id)) {
976
979
  throw new Error(`ssdiagram: duplicate link id "${id}"`);
977
980
  }
978
- const model = new LinkModel(link.from, link.fromPort, link.to, link.toPort, id, link.metadata);
981
+ const model = new LinkModel(link.from, link.fromPort, link.to, link.toPort, id, link.metadata, link.style ?? 'solid');
979
982
  if (!this.links.some((existing) => existing.key() === model.key()))
980
983
  this.links.push(model);
981
984
  }
@@ -1007,8 +1010,10 @@ export class Diagram {
1007
1010
  fromPort: link.from.portId,
1008
1011
  to: link.to.nodeId,
1009
1012
  toPort: link.to.portId,
1013
+ style: link.style,
1010
1014
  metadata: link.metadata,
1011
1015
  })));
1016
+ this.zones = document.zones.map((zone) => ({ ...zone, metadata: copyJsonObject(zone.metadata) }));
1012
1017
  this.documentMetadata = copyJsonObject(document.metadata);
1013
1018
  }
1014
1019
  saveDocument() {
@@ -1018,8 +1023,10 @@ export class Diagram {
1018
1023
  id: link.id,
1019
1024
  from: { nodeId: link.from, portId: link.fromPort },
1020
1025
  to: { nodeId: link.to, portId: link.toPort },
1026
+ style: link.style,
1021
1027
  metadata: link.metadata,
1022
1028
  })),
1029
+ zones: this.zones.map((zone) => ({ ...zone, metadata: copyJsonObject(zone.metadata) })),
1023
1030
  metadata: this.documentMetadata,
1024
1031
  });
1025
1032
  }
@@ -2388,7 +2395,10 @@ export class Diagram {
2388
2395
  if (options.grid)
2389
2396
  this.drawGrid();
2390
2397
  ctx.setTransform(this.dpr * this.scale, 0, 0, this.dpr * this.scale, this.dpr * this.offX, this.dpr * (this.offY + introDy));
2391
- // Links first. Each link jumps over the verticals of the links
2398
+ // Zones sit under everything: they say where things are, and a place that painted over
2399
+ // its occupants would be worse than no place at all.
2400
+ this.drawZones();
2401
+ // Links next. Each link jumps over the verticals of the links
2392
2402
  // drawn before it (deterministic bridge — Link.JumpOver parity).
2393
2403
  const prior = []; // segments of links already drawn
2394
2404
  const hoveredNode = this.hoverPort?.node ?? this.hoverNode;
@@ -2410,7 +2420,7 @@ export class Diagram {
2410
2420
  const color = state === 'sel' ? this.selectionColor() : state === 'hov' ? this.linkHoverColor() : baseColor;
2411
2421
  const width = state === 'sel' ? 3 : state === 'hov' ? 2.6 : 2;
2412
2422
  const pts = this.routeLink(a, b, new Set([l.from, l.to]));
2413
- this.strokeRoute(pts, color, width, prior);
2423
+ this.strokeRoute(pts, color, width, prior, l.style);
2414
2424
  this.drawArrow(pts, color);
2415
2425
  for (let k = 1; k < pts.length; k += 1) {
2416
2426
  const [x1, y1] = pts[k - 1];
@@ -2577,6 +2587,37 @@ export class Diagram {
2577
2587
  }
2578
2588
  return lines.length > 0 ? lines : [''];
2579
2589
  }
2590
+ drawZones() {
2591
+ if (this.zones.length === 0)
2592
+ return;
2593
+ const ctx = this.ctx;
2594
+ for (const zone of this.zones) {
2595
+ const tint = zone.color.length > 0 ? zone.color : (this.opts.zoneColor ?? '#8a8f98');
2596
+ ctx.save();
2597
+ roundRect(ctx, zone.x, zone.y, zone.width, zone.height, 10);
2598
+ ctx.globalAlpha = 0.12;
2599
+ ctx.fillStyle = tint;
2600
+ ctx.fill();
2601
+ ctx.globalAlpha = 0.5;
2602
+ ctx.setLineDash([10, 6]);
2603
+ ctx.lineWidth = 1.5;
2604
+ ctx.strokeStyle = tint;
2605
+ ctx.stroke();
2606
+ ctx.restore();
2607
+ if (zone.name.length === 0)
2608
+ continue;
2609
+ ctx.save();
2610
+ ctx.globalAlpha = 0.75;
2611
+ ctx.fillStyle = tint;
2612
+ ctx.font = '600 12px Segoe UI, Tahoma, sans-serif';
2613
+ ctx.textAlign = 'left';
2614
+ ctx.textBaseline = 'top';
2615
+ // Inside the top-left corner: a caption outside the box would collide with whatever
2616
+ // sits above it, and the corner is the one spot a rectangle always has to spare.
2617
+ ctx.fillText(zone.name, zone.x + 12, zone.y + 9, Math.max(0, zone.width - 24));
2618
+ ctx.restore();
2619
+ }
2620
+ }
2580
2621
  drawGrid() {
2581
2622
  const ctx = this.ctx;
2582
2623
  const step = this.gridSize * this.scale;
@@ -2718,10 +2759,13 @@ export class Diagram {
2718
2759
  // Symmetric jump-over: any segment of THIS link hops the perpendicular
2719
2760
  // segments of links drawn before it (H over earlier V, V over earlier
2720
2761
  // H). Exactly one bridge per real crossing, consistent everywhere.
2721
- strokeRoute(pts, color, width, prior) {
2762
+ strokeRoute(pts, color, width, prior, style = 'solid') {
2722
2763
  const ctx = this.ctx;
2723
2764
  ctx.strokeStyle = color;
2724
2765
  ctx.lineWidth = width;
2766
+ // The dash is measured in world units, so it keeps its rhythm as the canvas is zoomed.
2767
+ if (style === 'dashed')
2768
+ ctx.setLineDash([9, 6]);
2725
2769
  ctx.lineJoin = 'miter';
2726
2770
  ctx.lineCap = 'butt';
2727
2771
  ctx.beginPath();
@@ -2781,6 +2825,7 @@ export class Diagram {
2781
2825
  }
2782
2826
  }
2783
2827
  ctx.stroke();
2828
+ ctx.setLineDash([]);
2784
2829
  }
2785
2830
  drawArrow(pts, color) {
2786
2831
  const n = pts.length;