@html-graph/html-graph 8.19.1 → 8.21.0

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/README.md CHANGED
@@ -36,4 +36,8 @@ const canvas = new CanvasBuilder(element)
36
36
  .build();
37
37
  ```
38
38
 
39
- Visit the <a target="_blank" href="https://html-graph.github.io">DOCUMENTATION</a> for examples and API reference.
39
+ ## Links
40
+
41
+ - <a target="_blank" href="https://html-graph.github.io">Documentation</a>
42
+ - <a target="_blank" href="https://github.com/html-graph/html-graph/blob/master/CONTRIBUTING.md">Contributing</a>
43
+ - <a target="_blank" href="https://github.com/html-graph/html-graph/blob/master/DEVELOPMENT.md">Development</a>
@@ -152,6 +152,8 @@ declare type BezierEdgeShapeConfig = {
152
152
  readonly type?: "bezier" | undefined;
153
153
  } & BezierEdgeParams;
154
154
 
155
+ export declare const boxPortOffsetFn: PortOffsetFn;
156
+
155
157
  export declare class Canvas {
156
158
  readonly graph: Graph;
157
159
  readonly viewport: Viewport;
@@ -346,8 +348,8 @@ export declare interface DirectEdgeParams {
346
348
  readonly arrowRenderer?: ArrowRendererConfig | undefined;
347
349
  readonly hasSourceArrow?: boolean | undefined;
348
350
  readonly hasTargetArrow?: boolean | undefined;
349
- readonly sourceOffset?: number | undefined;
350
- readonly targetOffset?: number | undefined;
351
+ readonly sourceOffset?: PortOffset | undefined;
352
+ readonly targetOffset?: PortOffset | undefined;
351
353
  }
352
354
 
353
355
  export declare class DirectEdgeShape implements StructuredEdgeShape {
@@ -359,13 +361,14 @@ export declare class DirectEdgeShape implements StructuredEdgeShape {
359
361
  private readonly color;
360
362
  private readonly width;
361
363
  private readonly arrowLength;
362
- private readonly sourceOffset;
363
- private readonly targetOffset;
364
+ private readonly sourceOffsetFn;
365
+ private readonly targetOffsetFn;
364
366
  readonly onAfterRender: EventHandler<StructuredEdgeRenderModel>;
365
367
  private readonly afterRenderEmitter;
366
368
  private readonly arrowRenderer;
367
369
  constructor(params?: DirectEdgeParams);
368
370
  render(params: EdgeRenderParams): void;
371
+ private renderEmpty;
369
372
  }
370
373
 
371
374
  declare type DirectEdgeShapeConfig = {
@@ -851,10 +854,24 @@ export declare interface Point {
851
854
 
852
855
  export declare type PortElement = HTMLElement | SVGElement;
853
856
 
857
+ export declare type PortOffset = number | PortOffsetFn | "box";
858
+
859
+ export declare type PortOffsetFn = (params: PortOffsetFnParams) => number;
860
+
861
+ export declare interface PortOffsetFnParams {
862
+ readonly direction: Point;
863
+ readonly radius: Radii;
864
+ }
865
+
854
866
  declare type Priority = ConstantPriority | IncrementalPriority | CustomPriority;
855
867
 
856
868
  export declare type PriorityFn = () => number;
857
869
 
870
+ export declare interface Radii {
871
+ readonly horizontal: number;
872
+ readonly vertical: number;
873
+ }
874
+
858
875
  export declare interface ScaleLimitPreprocessorParams {
859
876
  readonly minContentScale: number | null;
860
877
  readonly maxContentScale: number | null;
@@ -1191,10 +1208,7 @@ export declare interface ViewportTransformConfig {
1191
1208
  }
1192
1209
 
1193
1210
  export declare interface VirtualScrollConfig {
1194
- readonly nodeContainingRadius: {
1195
- readonly vertical: number;
1196
- readonly horizontal: number;
1197
- };
1211
+ readonly nodeContainingRadius: Radii;
1198
1212
  readonly events?: {
1199
1213
  readonly onBeforeNodeAttached?: (nodeId: Identifier) => void;
1200
1214
  readonly onAfterNodeDetached?: (nodeId: Identifier) => void;
@@ -1806,58 +1806,6 @@ class CycleCircleEdgePath {
1806
1806
  this.midpoint = absPoints[3];
1807
1807
  }
1808
1808
  }
1809
- class DirectEdgePath {
1810
- constructor(params) {
1811
- __publicField(this, "path");
1812
- __publicField(this, "midpoint");
1813
- __publicField(this, "diagonalDistance");
1814
- const {
1815
- from,
1816
- to,
1817
- sourceOffset,
1818
- targetOffset,
1819
- hasSourceArrow,
1820
- hasTargetArrow,
1821
- arrowLength
1822
- } = params;
1823
- this.midpoint = { x: (from.x + to.x) / 2, y: (from.y + to.y) / 2 };
1824
- const width = to.x - from.x;
1825
- const height = to.y - from.y;
1826
- this.diagonalDistance = Math.sqrt(width * width + height * height);
1827
- if (this.diagonalDistance === 0) {
1828
- this.path = "";
1829
- return;
1830
- }
1831
- const dir = { x: width, y: height };
1832
- const source = this.createDirectLinePoint({
1833
- offset: sourceOffset,
1834
- hasArrow: hasSourceArrow,
1835
- flip: 1,
1836
- shift: from,
1837
- arrowLength,
1838
- dir
1839
- });
1840
- const target = this.createDirectLinePoint({
1841
- offset: targetOffset,
1842
- hasArrow: hasTargetArrow,
1843
- flip: -1,
1844
- shift: to,
1845
- arrowLength,
1846
- dir
1847
- });
1848
- this.path = `M ${source.x} ${source.y} L ${target.x} ${target.y}`;
1849
- }
1850
- createDirectLinePoint(params) {
1851
- const arrowOffset = params.hasArrow ? params.arrowLength : 0;
1852
- const totalOffset = params.offset + arrowOffset;
1853
- const targetRatio = params.flip * totalOffset / this.diagonalDistance;
1854
- const { dir, shift } = params;
1855
- return {
1856
- x: dir.x * targetRatio + shift.x,
1857
- y: dir.y * targetRatio + shift.y
1858
- };
1859
- }
1860
- }
1861
1809
  const calculateDotourY = (from, to, detourDistance) => {
1862
1810
  const max = Math.max(from.y, to.y);
1863
1811
  const min = Math.min(from.y, to.y);
@@ -2005,7 +1953,7 @@ const edgeConstants = Object.freeze({
2005
1953
  smallCycleRadius: 15,
2006
1954
  curvature: 90,
2007
1955
  interactiveWidth: 10,
2008
- preOffset: 0
1956
+ portOffset: 0
2009
1957
  });
2010
1958
  const createDirectionVector = (dir) => {
2011
1959
  return { x: Math.cos(dir), y: Math.sin(dir) };
@@ -2546,6 +2494,27 @@ class VerticalEdgeShape {
2546
2494
  });
2547
2495
  }
2548
2496
  }
2497
+ const boxPortOffsetFn = (params) => {
2498
+ const { direction, radius } = params;
2499
+ const { x, y } = direction;
2500
+ const { horizontal, vertical } = radius;
2501
+ const tg = y / x;
2502
+ const horX = Math.abs(vertical / tg);
2503
+ const vertY = Math.abs(horizontal * tg);
2504
+ const minX = Math.min(horizontal, horX);
2505
+ const minY = Math.min(vertical, vertY);
2506
+ return Math.sqrt(minX * minX + minY * minY);
2507
+ };
2508
+ const resolvePortOffsetFn = (offset) => {
2509
+ if (typeof offset === "number") {
2510
+ return () => offset;
2511
+ }
2512
+ if (offset === "box") {
2513
+ return boxPortOffsetFn;
2514
+ }
2515
+ return offset;
2516
+ };
2517
+ const defaultPortOffset = edgeConstants.portOffset;
2549
2518
  class DirectEdgeShape {
2550
2519
  constructor(params) {
2551
2520
  __publicField(this, "svg");
@@ -2559,8 +2528,8 @@ class DirectEdgeShape {
2559
2528
  __publicField(this, "color");
2560
2529
  __publicField(this, "width");
2561
2530
  __publicField(this, "arrowLength");
2562
- __publicField(this, "sourceOffset");
2563
- __publicField(this, "targetOffset");
2531
+ __publicField(this, "sourceOffsetFn");
2532
+ __publicField(this, "targetOffsetFn");
2564
2533
  __publicField(this, "onAfterRender");
2565
2534
  __publicField(this, "afterRenderEmitter");
2566
2535
  __publicField(this, "arrowRenderer");
@@ -2569,8 +2538,12 @@ class DirectEdgeShape {
2569
2538
  this.width = (params == null ? void 0 : params.width) ?? edgeConstants.width;
2570
2539
  this.arrowLength = (params == null ? void 0 : params.arrowLength) ?? edgeConstants.arrowLength;
2571
2540
  this.arrowRenderer = resolveArrowRenderer((params == null ? void 0 : params.arrowRenderer) ?? {});
2572
- this.sourceOffset = (params == null ? void 0 : params.sourceOffset) ?? edgeConstants.preOffset;
2573
- this.targetOffset = (params == null ? void 0 : params.targetOffset) ?? edgeConstants.preOffset;
2541
+ this.sourceOffsetFn = resolvePortOffsetFn(
2542
+ (params == null ? void 0 : params.sourceOffset) ?? defaultPortOffset
2543
+ );
2544
+ this.targetOffsetFn = resolvePortOffsetFn(
2545
+ (params == null ? void 0 : params.targetOffset) ?? defaultPortOffset
2546
+ );
2574
2547
  this.svg = createEdgeSvg(this.color);
2575
2548
  this.svg.appendChild(this.group);
2576
2549
  this.line = createEdgePath(this.width);
@@ -2591,63 +2564,102 @@ class DirectEdgeShape {
2591
2564
  svgPadding
2592
2565
  );
2593
2566
  setSvgRectangle(this.svg, { x, y, width, height });
2594
- const edgePath = new DirectEdgePath({
2595
- from,
2596
- to,
2597
- sourceOffset: this.sourceOffset,
2598
- targetOffset: this.targetOffset,
2599
- hasSourceArrow: this.sourceArrow !== null,
2600
- hasTargetArrow: this.targetArrow !== null,
2601
- arrowLength: this.arrowLength
2602
- });
2603
- this.line.setAttribute("d", edgePath.path);
2604
- let sourceArrowPath = null;
2605
- let targetArrowPath = null;
2606
- const diagonal = edgePath.diagonalDistance;
2567
+ const dirX = to.x - from.x;
2568
+ const dirY = to.y - from.y;
2569
+ const diagonal = Math.sqrt(dirX * dirX + dirY * dirY);
2607
2570
  if (diagonal === 0) {
2608
- if (this.sourceArrow !== null) {
2609
- sourceArrowPath = "";
2610
- this.sourceArrow.setAttribute("d", sourceArrowPath);
2571
+ this.renderEmpty(from);
2572
+ return;
2573
+ }
2574
+ const direction = { x: dirX / diagonal, y: dirY / diagonal };
2575
+ const sourceOffset = this.sourceOffsetFn({
2576
+ direction: { x: direction.x, y: direction.y },
2577
+ radius: {
2578
+ horizontal: params.from.width / 2,
2579
+ vertical: params.from.height / 2
2611
2580
  }
2612
- if (this.targetArrow !== null) {
2613
- targetArrowPath = "";
2614
- this.targetArrow.setAttribute("d", targetArrowPath);
2581
+ });
2582
+ const targetOffset = this.targetOffsetFn({
2583
+ direction: { x: -direction.x, y: -direction.y },
2584
+ radius: {
2585
+ horizontal: params.to.width / 2,
2586
+ vertical: params.to.height / 2
2615
2587
  }
2616
- } else {
2617
- const direction = {
2618
- x: (to.x - from.x) / diagonal,
2619
- y: (to.y - from.y) / diagonal
2588
+ });
2589
+ const source = {
2590
+ x: from.x + sourceOffset * direction.x,
2591
+ y: from.y + sourceOffset * direction.y
2592
+ };
2593
+ const target = {
2594
+ x: to.x - targetOffset * direction.x,
2595
+ y: to.y - targetOffset * direction.y
2596
+ };
2597
+ const diagonalSource = this.sourceArrow !== null ? this.arrowLength : 0;
2598
+ const sourceLine = {
2599
+ x: source.x + diagonalSource * direction.x,
2600
+ y: source.y + diagonalSource * direction.y
2601
+ };
2602
+ const diagonalTarget = this.targetArrow !== null ? this.arrowLength : 0;
2603
+ const targetLine = {
2604
+ x: target.x - diagonalTarget * direction.x,
2605
+ y: target.y - diagonalTarget * direction.y
2606
+ };
2607
+ const midpoint = {
2608
+ x: (source.x + target.x) / 2,
2609
+ y: (source.y + target.y) / 2
2610
+ };
2611
+ const path = `M ${sourceLine.x} ${sourceLine.y} L ${targetLine.x} ${targetLine.y}`;
2612
+ this.line.setAttribute("d", path);
2613
+ let sourceArrowPath = null;
2614
+ let targetArrowPath = null;
2615
+ if (this.sourceArrow) {
2616
+ const sourceOffsetPoint = {
2617
+ x: direction.x * sourceOffset + from.x,
2618
+ y: direction.y * sourceOffset + from.y
2620
2619
  };
2621
- if (this.sourceArrow) {
2622
- const sourceOffset = {
2623
- x: direction.x * this.sourceOffset + from.x,
2624
- y: direction.y * this.sourceOffset + from.y
2625
- };
2626
- sourceArrowPath = this.arrowRenderer({
2627
- direction,
2628
- shift: sourceOffset,
2629
- arrowLength: this.arrowLength
2630
- });
2631
- this.sourceArrow.setAttribute("d", sourceArrowPath);
2632
- }
2633
- if (this.targetArrow) {
2634
- const targetOffset = {
2635
- x: direction.x * this.targetOffset,
2636
- y: direction.y * this.targetOffset
2637
- };
2638
- targetArrowPath = this.arrowRenderer({
2639
- direction: { x: -direction.x, y: -direction.y },
2640
- shift: {
2641
- x: to.x - targetOffset.x,
2642
- y: to.y - targetOffset.y
2643
- },
2644
- arrowLength: this.arrowLength
2645
- });
2646
- this.targetArrow.setAttribute("d", targetArrowPath);
2647
- }
2620
+ sourceArrowPath = this.arrowRenderer({
2621
+ direction,
2622
+ shift: sourceOffsetPoint,
2623
+ arrowLength: this.arrowLength
2624
+ });
2625
+ this.sourceArrow.setAttribute("d", sourceArrowPath);
2626
+ }
2627
+ if (this.targetArrow) {
2628
+ const targetOffsetPoint = {
2629
+ x: direction.x * targetOffset,
2630
+ y: direction.y * targetOffset
2631
+ };
2632
+ targetArrowPath = this.arrowRenderer({
2633
+ direction: { x: -direction.x, y: -direction.y },
2634
+ shift: {
2635
+ x: to.x - targetOffsetPoint.x,
2636
+ y: to.y - targetOffsetPoint.y
2637
+ },
2638
+ arrowLength: this.arrowLength
2639
+ });
2640
+ this.targetArrow.setAttribute("d", targetArrowPath);
2648
2641
  }
2649
2642
  this.afterRenderEmitter.emit({
2650
- edgePath,
2643
+ edgePath: { path, midpoint },
2644
+ sourceArrowPath,
2645
+ targetArrowPath
2646
+ });
2647
+ }
2648
+ renderEmpty(midpoint) {
2649
+ const emptyPath = "";
2650
+ let sourceArrowPath = null;
2651
+ let targetArrowPath = null;
2652
+ this.line.setAttribute("d", emptyPath);
2653
+ if (this.sourceArrow !== null) {
2654
+ sourceArrowPath = "";
2655
+ this.sourceArrow.setAttribute("d", sourceArrowPath);
2656
+ }
2657
+ if (this.targetArrow !== null) {
2658
+ targetArrowPath = "";
2659
+ this.targetArrow.setAttribute("d", targetArrowPath);
2660
+ }
2661
+ this.afterRenderEmitter.emit({
2662
+ edgePath: { path: emptyPath, midpoint },
2651
2663
  sourceArrowPath,
2652
2664
  targetArrowPath
2653
2665
  });
@@ -7109,5 +7121,6 @@ export {
7109
7121
  InteractiveEdgeShape,
7110
7122
  MidpointEdgeShape,
7111
7123
  StraightEdgeShape,
7112
- VerticalEdgeShape
7124
+ VerticalEdgeShape,
7125
+ boxPortOffsetFn
7113
7126
  };