orcasvn-react-diagrams 0.2.3 → 0.2.4

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.
Files changed (32) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +15 -11
  3. package/dist/cjs/examples.js +857 -139
  4. package/dist/cjs/index.js +408 -41
  5. package/dist/cjs/types/api/types.d.ts +9 -0
  6. package/dist/cjs/types/displaybox/demos/labelStyleDemo.d.ts +2 -0
  7. package/dist/cjs/types/displaybox/demos/portPositionLimitsDemo.d.ts +2 -0
  8. package/dist/cjs/types/engine/DiagramEngine.d.ts +6 -0
  9. package/dist/cjs/types/engine/LinkRoutingService.d.ts +1 -1
  10. package/dist/cjs/types/renderer/konva/KonvaNodeFactory.d.ts +11 -0
  11. package/dist/cjs/types/renderer/konva/KonvaRenderer.d.ts +2 -0
  12. package/dist/cjs/types/strategies/ObstacleRouter.d.ts +2 -0
  13. package/dist/esm/examples.js +857 -139
  14. package/dist/esm/examples.js.map +1 -1
  15. package/dist/esm/index.js +408 -41
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/types/api/types.d.ts +9 -0
  18. package/dist/esm/types/displaybox/demos/labelStyleDemo.d.ts +2 -0
  19. package/dist/esm/types/displaybox/demos/portPositionLimitsDemo.d.ts +2 -0
  20. package/dist/esm/types/engine/DiagramEngine.d.ts +6 -0
  21. package/dist/esm/types/engine/LinkRoutingService.d.ts +1 -1
  22. package/dist/esm/types/renderer/konva/KonvaNodeFactory.d.ts +11 -0
  23. package/dist/esm/types/renderer/konva/KonvaRenderer.d.ts +2 -0
  24. package/dist/esm/types/strategies/ObstacleRouter.d.ts +2 -0
  25. package/dist/examples.d.ts +9 -0
  26. package/dist/index.d.ts +10 -1
  27. package/package.json +11 -11
  28. package/src/displaybox/demos/ObstacleRoutingDemoTab.tsx +11 -10
  29. package/src/displaybox/demos/index.tsx +27 -13
  30. package/src/displaybox/demos/labelStyleDemo.ts +101 -0
  31. package/src/displaybox/demos/obstacleRoutingDemo.ts +212 -176
  32. package/src/displaybox/demos/portPositionLimitsDemo.ts +211 -0
@@ -48,6 +48,14 @@ export type PortAnchorConstraint = {
48
48
  preset: HostAnchorPreset;
49
49
  fallback?: 'nearest';
50
50
  };
51
+ export type PositionAxisLimit = {
52
+ min?: number;
53
+ max?: number;
54
+ };
55
+ export type PortPositionLimits = {
56
+ x?: PositionAxisLimit;
57
+ y?: PositionAxisLimit;
58
+ };
51
59
  export type ResolvePortAnchorsOptions = {
52
60
  preset: HostAnchorPreset;
53
61
  };
@@ -158,6 +166,7 @@ export type ElementShapeHoverControlActivationEvent = {
158
166
  export type ElementPortMovementPolicy = {
159
167
  moveMode: MoveConstraint | 'anchors';
160
168
  anchorConstraint?: PortAnchorConstraint;
169
+ positionLimits?: PortPositionLimits;
161
170
  };
162
171
  export type LinkRoutingMode = 'auto' | 'manual';
163
172
  export type AnchorReference = 'top-left' | 'center';
@@ -0,0 +1,2 @@
1
+ import type { DemoConfig } from '../types';
2
+ export declare const labelStyleDemoConfig: DemoConfig;
@@ -0,0 +1,2 @@
1
+ import type { DemoConfig } from '../types';
2
+ export declare const portPositionLimitsDemoConfig: DemoConfig;
@@ -115,12 +115,17 @@ export default class DiagramEngine {
115
115
  private collectResizedElementSizes;
116
116
  private resolveBorderPortResizeProjection;
117
117
  private resolveConstrainedPortRelativePosition;
118
+ private applyPortPositionLimits;
119
+ private resolveBorderPositionWithLimits;
120
+ private isPointWithinPositionLimits;
121
+ private filterAnchorsByPositionLimits;
118
122
  private resolveEffectivePortMoveMode;
119
123
  private resolvePortAnchorsForElement;
120
124
  private resolveNearestPortAnchor;
121
125
  private findNearestPortAnchor;
122
126
  private resolveAnchoredPortResizeProjection;
123
127
  private resolveTextPresentation;
128
+ private resolveAllTextPresentationPatches;
124
129
  private emitSelection;
125
130
  private applyLayoutForParent;
126
131
  private applyLayoutCascade;
@@ -132,6 +137,7 @@ export default class DiagramEngine {
132
137
  private constrainPortToHostBorder;
133
138
  private projectPointToHostBorder;
134
139
  private routeLinksWithEmptyPoints;
140
+ private normalizePortsForHostPolicies;
135
141
  private computeRemovalDiff;
136
142
  private emitEntityDeletionEvents;
137
143
  }
@@ -34,7 +34,7 @@ export default class LinkRoutingService {
34
34
  private getElementRect;
35
35
  private resolveHostForPort;
36
36
  private resolveAttachModeForPorts;
37
- private hasAncestorRelation;
37
+ private isAncestorOf;
38
38
  private getAncestorChain;
39
39
  private getAncestorExclusions;
40
40
  private resolveRouteBounds;
@@ -36,6 +36,17 @@ export default class KonvaNodeFactory {
36
36
  createPortNode(model: PortData): KonvaNodeLike;
37
37
  createLinkNode(model: LinkData): KonvaNodeLike;
38
38
  createTextNode(model: TextData): KonvaNodeLike;
39
+ createTextBackgroundNode(config: {
40
+ id: string;
41
+ x: number;
42
+ y: number;
43
+ width: number;
44
+ height: number;
45
+ fill: string;
46
+ stroke?: string;
47
+ strokeWidth?: number;
48
+ cornerRadius?: number | [number, number, number, number];
49
+ }): KonvaNodeLike;
39
50
  createHandleNode(config: {
40
51
  id: string;
41
52
  x: number;
@@ -61,6 +61,7 @@ export default class KonvaRenderer implements Renderer {
61
61
  private portNodes;
62
62
  private linkNodes;
63
63
  private textNodes;
64
+ private textBackgroundNodes;
64
65
  private selectedIds;
65
66
  private overlayLayer;
66
67
  private tempLinkNode;
@@ -118,6 +119,7 @@ export default class KonvaRenderer implements Renderer {
118
119
  private syncPorts;
119
120
  private syncLinks;
120
121
  private syncTexts;
122
+ private resolveTextBackgroundMeta;
121
123
  private updatePosition;
122
124
  private applyPortOrientation;
123
125
  private resolveHostElement;
@@ -16,6 +16,8 @@ export default class ObstacleRouter implements RouterStrategy {
16
16
  private pointsWithinBounds;
17
17
  private isPathClear;
18
18
  private computeStubEndpoint;
19
+ private computeAvailableStubLength;
20
+ private rectsEqual;
19
21
  private getNormal;
20
22
  private distanceToBounds;
21
23
  private distanceToObstacles;
@@ -50,6 +50,14 @@ type PortAnchorConstraint = {
50
50
  preset: HostAnchorPreset;
51
51
  fallback?: 'nearest';
52
52
  };
53
+ type PositionAxisLimit = {
54
+ min?: number;
55
+ max?: number;
56
+ };
57
+ type PortPositionLimits = {
58
+ x?: PositionAxisLimit;
59
+ y?: PositionAxisLimit;
60
+ };
53
61
  type ResolvePortAnchorsOptions = {
54
62
  preset: HostAnchorPreset;
55
63
  };
@@ -131,6 +139,7 @@ type ElementShapeHoverControls = {
131
139
  type ElementPortMovementPolicy = {
132
140
  moveMode: MoveConstraint | 'anchors';
133
141
  anchorConstraint?: PortAnchorConstraint;
142
+ positionLimits?: PortPositionLimits;
134
143
  };
135
144
  type LinkRoutingMode = 'auto' | 'manual';
136
145
  type AnchorReference = 'top-left' | 'center';
package/dist/index.d.ts CHANGED
@@ -48,6 +48,14 @@ type PortAnchorConstraint = {
48
48
  preset: HostAnchorPreset;
49
49
  fallback?: 'nearest';
50
50
  };
51
+ type PositionAxisLimit = {
52
+ min?: number;
53
+ max?: number;
54
+ };
55
+ type PortPositionLimits = {
56
+ x?: PositionAxisLimit;
57
+ y?: PositionAxisLimit;
58
+ };
51
59
  type ResolvePortAnchorsOptions = {
52
60
  preset: HostAnchorPreset;
53
61
  };
@@ -158,6 +166,7 @@ type ElementShapeHoverControlActivationEvent = {
158
166
  type ElementPortMovementPolicy = {
159
167
  moveMode: MoveConstraint | 'anchors';
160
168
  anchorConstraint?: PortAnchorConstraint;
169
+ positionLimits?: PortPositionLimits;
161
170
  };
162
171
  type LinkRoutingMode = 'auto' | 'manual';
163
172
  type AnchorReference = 'top-left' | 'center';
@@ -767,4 +776,4 @@ type DiagramEditorHandle = DiagramEngineHandle & {
767
776
  };
768
777
  declare const createDiagramEditor: (config: DiagramEditorConfig) => DiagramEditorHandle;
769
778
 
770
- export { type AnchorReference, type BorderSide$1 as BorderSide, type ClientRectLike, type DiagramContainer, type DiagramEditorConfig, type DiagramEditorHandle, type DiagramEngineHandle, type DiagramImageExportOptions, type DiagramPatch, type DiagramState, type EdgeHoverControl, type ElementData, type ElementDeletedEvent, type ElementDropEvent, type ElementLayout, type ElementLayoutAlign, type ElementLayoutChildFitCrossAxis, type ElementLayoutChildFitMainAxis, type ElementLayoutLabelReservedSpace, type ElementLayoutLabelReservedSpaceMode, type ElementLayoutLabelReservedSpacePlacement, type ElementLayoutMode, type ElementLinkConnectingEvent, type ElementLinkEndedEvent, type ElementLinkStartedEvent, type ElementMovedEvent, type ElementPointerEvent, type ElementPortMovementPolicy, type ElementResizedEvent, type ElementSelectedEvent, type ElementShapeControlDragEvent, type ElementShapeControlEventType, type ElementShapeHoverControlActivationEvent, type ElementShapeHoverControlInteractionEvent, type ElementShapeHoverControls, EllipseMidPoint, type EngineChangeEvent, type EngineConfigEvent, type EngineEventMap, type EnginePointerInfo, type EngineSelectionEvent, type HostAnchorPreset, type HoverControlIcon, type LinkData, type LinkDeletedEvent, type LinkRoutingMode, type MoveConstraint, type OverlayShapeConfig, type OverlayShapeHandle, type PaperClickEvent, type Point, type PortAnchor, type PortAnchorConstraint, type PortBorderTransformContext, type PortBorderTransformResult, type PortData, type PortDeletedEvent, type PortLinkAttachMode, type PortMouseEvent, type PortMovedEvent, type PortSelectedEvent, type Rect, type RerouteLinksOptions, type ResolvePortAnchorsOptions, type ShapeControlDefinition, type ShapeControlTargetKind, type ShapeControlVisibilityTrigger, type ShapeDrawContext, type ShapeEdgeTarget, type ShapeEllipseMidPointTarget, type ShapeHoverGeometry, type ShapeVertexTarget, type SimpleShape, type Size, type TextData, type TextDeletedEvent, type TextLayout, type TextLayoutBoundsMode, type TextLayoutOverflowMode, type TextLayoutWrapMode, type TextSelectedEvent, type TextUpdatedEvent, type VertexHoverControl, createDiagramEditor, createDiagramEngine };
779
+ export { type AnchorReference, type BorderSide$1 as BorderSide, type ClientRectLike, type DiagramContainer, type DiagramEditorConfig, type DiagramEditorHandle, type DiagramEngineHandle, type DiagramImageExportOptions, type DiagramPatch, type DiagramState, type EdgeHoverControl, type ElementData, type ElementDeletedEvent, type ElementDropEvent, type ElementLayout, type ElementLayoutAlign, type ElementLayoutChildFitCrossAxis, type ElementLayoutChildFitMainAxis, type ElementLayoutLabelReservedSpace, type ElementLayoutLabelReservedSpaceMode, type ElementLayoutLabelReservedSpacePlacement, type ElementLayoutMode, type ElementLinkConnectingEvent, type ElementLinkEndedEvent, type ElementLinkStartedEvent, type ElementMovedEvent, type ElementPointerEvent, type ElementPortMovementPolicy, type ElementResizedEvent, type ElementSelectedEvent, type ElementShapeControlDragEvent, type ElementShapeControlEventType, type ElementShapeHoverControlActivationEvent, type ElementShapeHoverControlInteractionEvent, type ElementShapeHoverControls, EllipseMidPoint, type EngineChangeEvent, type EngineConfigEvent, type EngineEventMap, type EnginePointerInfo, type EngineSelectionEvent, type HostAnchorPreset, type HoverControlIcon, type LinkData, type LinkDeletedEvent, type LinkRoutingMode, type MoveConstraint, type OverlayShapeConfig, type OverlayShapeHandle, type PaperClickEvent, type Point, type PortAnchor, type PortAnchorConstraint, type PortBorderTransformContext, type PortBorderTransformResult, type PortData, type PortDeletedEvent, type PortLinkAttachMode, type PortMouseEvent, type PortMovedEvent, type PortPositionLimits, type PortSelectedEvent, type PositionAxisLimit, type Rect, type RerouteLinksOptions, type ResolvePortAnchorsOptions, type ShapeControlDefinition, type ShapeControlTargetKind, type ShapeControlVisibilityTrigger, type ShapeDrawContext, type ShapeEdgeTarget, type ShapeEllipseMidPointTarget, type ShapeHoverGeometry, type ShapeVertexTarget, type SimpleShape, type Size, type TextData, type TextDeletedEvent, type TextLayout, type TextLayoutBoundsMode, type TextLayoutOverflowMode, type TextLayoutWrapMode, type TextSelectedEvent, type TextUpdatedEvent, type VertexHoverControl, createDiagramEditor, createDiagramEngine };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orcasvn-react-diagrams",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "dependencies": {
5
5
  "eventemitter3": "^5.0.1",
6
6
  "flatten-js": "^0.6.9",
@@ -57,16 +57,16 @@
57
57
  "./ai-manifest": "./ai/manifest.json",
58
58
  "./package.json": "./package.json"
59
59
  },
60
- "files": [
61
- "dist",
62
- "src/examples",
63
- "src/displaybox/demos",
64
- "src/displaybox/types.ts",
65
- "README.md",
66
- "CHANGELOG.md",
67
- "docs",
68
- "ai"
69
- ],
60
+ "files": [
61
+ "dist",
62
+ "src/examples",
63
+ "src/displaybox/demos",
64
+ "src/displaybox/types.ts",
65
+ "README.md",
66
+ "CHANGELOG.md",
67
+ "docs",
68
+ "ai"
69
+ ],
70
70
  "types": "dist/index.d.ts",
71
71
  "devDependencies": {
72
72
  "@rollup/plugin-commonjs": "^26.0.1",
@@ -15,16 +15,17 @@ const ObstacleRoutingDemo = () => (
15
15
  background: '#f4f4f4',
16
16
  }}
17
17
  >
18
- <strong>Expected routing behavior</strong>
19
- <ul style={{ margin: '6px 0 0 16px' }}>
20
- <li>Links between top nodes should route around the blocking element.</li>
21
- <li>Sibling links inside a shared parent should ignore the parent as an obstacle.</li>
22
- <li>Parent-child links should stay within the parent and avoid the child interior when ports are on edges.</li>
23
- <li>Grandchild links should ignore shared ancestors (parent + grandparent containers).</li>
24
- </ul>
25
- </div>
26
- }
27
- />
18
+ <strong>Expected routing behavior</strong>
19
+ <ul style={{ margin: '6px 0 0 16px' }}>
20
+ <li>Scenario A: the sibling link must not cross either child host interior.</li>
21
+ <li>Scenario A: sibling host ports use anchor preset `cardinal` (left/right) for deterministic multi-anchor placement.</li>
22
+ <li>Scenario B has one parent and two children with three links: parent-&gt;child, child-&gt;parent, and child-&gt;child.</li>
23
+ <li>Scenario B: ancestor endpoints resolve to `internalLinkAttachPoint`; descendant/sibling endpoints resolve to `externalLinkAttachPoint`.</li>
24
+ <li>Use `Reroute All Links` repeatedly; both routes should stay deterministic after each reroute.</li>
25
+ </ul>
26
+ </div>
27
+ }
28
+ />
28
29
  );
29
30
 
30
31
  export default ObstacleRoutingDemo;
@@ -18,9 +18,11 @@ import { rotatedCreationDemoConfig } from './rotatedCreationDemo';
18
18
  import { multiLevelTreeDemoConfig } from './multiLevelTreeDemo';
19
19
  import { multipleElementsDemoConfig } from './multipleElementsDemo';
20
20
  import { asymmetricPortMultiAnchorDemoConfig } from './asymmetricPortMultiAnchorDemo';
21
- import { portConstraintsDemoConfig } from './portConstraintsDemo';
22
- import { portBorderDemoConfig } from './portBorderDemo';
23
- import { childConstraintsDemoConfig } from './childConstraintsDemo';
21
+ import { portConstraintsDemoConfig } from './portConstraintsDemo';
22
+ import { portPositionLimitsDemoConfig } from './portPositionLimitsDemo';
23
+ import { portBorderDemoConfig } from './portBorderDemo';
24
+ import { childConstraintsDemoConfig } from './childConstraintsDemo';
25
+ import { labelStyleDemoConfig } from './labelStyleDemo';
24
26
  import { gridOverlayDemoConfig } from './gridOverlayDemo';
25
27
  import { routingDemoConfig } from './routingDemo';
26
28
  import { obstacleRoutingDemoConfig } from './obstacleRoutingDemo';
@@ -170,16 +172,28 @@ export const demoTabs: DemoDefinition[] = [
170
172
  description: multipleElementsDemoConfig.description,
171
173
  Component: wrapSimpleDemo(multipleElementsDemoConfig),
172
174
  },
173
- {
174
- id: portConstraintsDemoConfig.id,
175
- title: portConstraintsDemoConfig.title,
176
- description: portConstraintsDemoConfig.description,
177
- Component: wrapSimpleDemo(portConstraintsDemoConfig),
178
- },
179
- {
180
- id: portBorderDemoConfig.id,
181
- title: portBorderDemoConfig.title,
182
- description: portBorderDemoConfig.description,
175
+ {
176
+ id: portConstraintsDemoConfig.id,
177
+ title: portConstraintsDemoConfig.title,
178
+ description: portConstraintsDemoConfig.description,
179
+ Component: wrapSimpleDemo(portConstraintsDemoConfig),
180
+ },
181
+ {
182
+ id: portPositionLimitsDemoConfig.id,
183
+ title: portPositionLimitsDemoConfig.title,
184
+ description: portPositionLimitsDemoConfig.description,
185
+ Component: wrapSimpleDemo(portPositionLimitsDemoConfig),
186
+ },
187
+ {
188
+ id: labelStyleDemoConfig.id,
189
+ title: labelStyleDemoConfig.title,
190
+ description: labelStyleDemoConfig.description,
191
+ Component: wrapSimpleDemo(labelStyleDemoConfig),
192
+ },
193
+ {
194
+ id: portBorderDemoConfig.id,
195
+ title: portBorderDemoConfig.title,
196
+ description: portBorderDemoConfig.description,
183
197
  Component: wrapSimpleDemo(portBorderDemoConfig),
184
198
  },
185
199
  {
@@ -0,0 +1,101 @@
1
+ import type { DiagramState } from '../../api';
2
+ import type { DemoConfig } from '../types';
3
+ import { baseElementShapes, basePortShapes } from './shared';
4
+
5
+ const createLabelStyleState = (): DiagramState => ({
6
+ elements: [
7
+ {
8
+ id: 'label-center-host',
9
+ position: { x: 80, y: 160 },
10
+ size: { width: 260, height: 120 },
11
+ shapeId: 'panel',
12
+ },
13
+ {
14
+ id: 'label-background-host',
15
+ position: { x: 390, y: 160 },
16
+ size: { width: 260, height: 120 },
17
+ shapeId: 'panel',
18
+ style: {
19
+ cornerRadius: 16,
20
+ stroke: '#1f4d99',
21
+ strokeWidth: 2,
22
+ fill: '#edf4ff',
23
+ },
24
+ },
25
+ {
26
+ id: 'label-baseline-host',
27
+ position: { x: 700, y: 160 },
28
+ size: { width: 260, height: 120 },
29
+ shapeId: 'panel',
30
+ },
31
+ ],
32
+ ports: [],
33
+ links: [],
34
+ texts: [
35
+ {
36
+ id: 'label-style-intro',
37
+ content: 'Compare center alignment, background fill, and baseline label styling. Resize hosts to verify visual stability.',
38
+ position: { x: 80, y: 80 },
39
+ },
40
+ {
41
+ id: 'label-center-text',
42
+ content: 'Center aligned owner-width label',
43
+ ownerId: 'label-center-host',
44
+ position: { x: 0, y: 44 },
45
+ layout: { boundsMode: 'owner-width', wrap: 'none', overflow: 'ellipsis-end', padding: 8 },
46
+ style: { align: 'center', fill: '#16324f', fontSize: 15 },
47
+ },
48
+ {
49
+ id: 'label-background-text',
50
+ content: 'Top Header Label',
51
+ ownerId: 'label-background-host',
52
+ position: { x: 0, y: 0 },
53
+ layout: { boundsMode: 'owner-width', wrap: 'none', overflow: 'ellipsis-end', padding: 0 },
54
+ style: {
55
+ align: 'center',
56
+ backgroundFill: '#cfe2ff',
57
+ backgroundStroke: '#1f4d99',
58
+ backgroundStrokeWidth: 1,
59
+ fill: '#16324f',
60
+ fontSize: 15,
61
+ padding: 8,
62
+ },
63
+ },
64
+ {
65
+ id: 'label-baseline-text',
66
+ content: 'Baseline label (no style keys)',
67
+ ownerId: 'label-baseline-host',
68
+ position: { x: 12, y: 44 },
69
+ layout: { boundsMode: 'owner-width', wrap: 'none', overflow: 'ellipsis-end', padding: 8 },
70
+ },
71
+ ],
72
+ });
73
+
74
+ export const labelStyleDemoConfig: DemoConfig = {
75
+ id: 'label-style',
76
+ title: 'Label Style',
77
+ description: 'Center text alignment and backgroundFill styling for labels.',
78
+ createState: createLabelStyleState,
79
+ elementShapes: baseElementShapes,
80
+ portShapes: basePortShapes,
81
+ defaultElementShapeId: 'default',
82
+ defaultPortShapeId: 'port-circle',
83
+ actions: [
84
+ {
85
+ id: 'resize-label-hosts',
86
+ label: 'Resize hosts (style verification)',
87
+ run: (editor, state) => {
88
+ const expanded = (state.elements.find((item) => item.id === 'label-center-host')?.size.width ?? 0) > 260;
89
+ if (expanded) {
90
+ editor.resizeElement('label-center-host', 260, 120);
91
+ editor.resizeElement('label-background-host', 260, 120);
92
+ editor.resizeElement('label-baseline-host', 260, 120);
93
+ return;
94
+ }
95
+ editor.resizeElement('label-center-host', 320, 140);
96
+ editor.resizeElement('label-background-host', 320, 140);
97
+ editor.resizeElement('label-baseline-host', 320, 140);
98
+ },
99
+ },
100
+ ],
101
+ };