orcasvn-react-diagrams 0.2.2 → 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 (83) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +15 -3
  3. package/dist/cjs/examples.js +2616 -291
  4. package/dist/cjs/index.js +1186 -153
  5. package/dist/cjs/types/api/createDiagramEditor.d.ts +19 -1
  6. package/dist/cjs/types/api/index.d.ts +1 -1
  7. package/dist/cjs/types/api/types.d.ts +41 -0
  8. package/dist/cjs/types/displaybox/DisplayBoxControls.d.ts +5 -1
  9. package/dist/cjs/types/displaybox/demos/AsymmetricPortMultiAnchorDemoTab.d.ts +3 -0
  10. package/dist/cjs/types/displaybox/demos/LayoutLabelReservedSpaceDemoTab.d.ts +3 -0
  11. package/dist/cjs/types/displaybox/demos/VertexControlLinkSessionDemoTab.d.ts +3 -0
  12. package/dist/cjs/types/displaybox/demos/asymmetricPortMultiAnchorDemo.d.ts +31 -0
  13. package/dist/cjs/types/displaybox/demos/labelStyleDemo.d.ts +2 -0
  14. package/dist/cjs/types/displaybox/demos/layoutLabelReservedSpaceDemo.d.ts +11 -0
  15. package/dist/cjs/types/displaybox/demos/portPositionLimitsDemo.d.ts +2 -0
  16. package/dist/cjs/types/displaybox/demos/vertexControlLinkSessionDemo.d.ts +12 -0
  17. package/dist/cjs/types/displaybox/useDemoControls.d.ts +4 -0
  18. package/dist/cjs/types/engine/AutoLayoutService.d.ts +2 -0
  19. package/dist/cjs/types/engine/DiagramEngine.d.ts +11 -0
  20. package/dist/cjs/types/engine/LinkRoutingService.d.ts +9 -1
  21. package/dist/cjs/types/models/PortModel.d.ts +5 -0
  22. package/dist/cjs/types/renderer/RenderTypes.d.ts +3 -1
  23. package/dist/cjs/types/renderer/konva/KonvaInteraction.d.ts +14 -0
  24. package/dist/cjs/types/renderer/konva/KonvaNodeFactory.d.ts +12 -0
  25. package/dist/cjs/types/renderer/konva/KonvaRenderer.d.ts +2 -1
  26. package/dist/cjs/types/shapes/BuiltInShapes.d.ts +3 -1
  27. package/dist/cjs/types/strategies/ObstacleRouter.d.ts +2 -0
  28. package/dist/cjs/types/utils/__tests__/portGeometry.test.d.ts +1 -0
  29. package/dist/cjs/types/utils/portGeometry.d.ts +44 -0
  30. package/dist/esm/examples.js +2617 -292
  31. package/dist/esm/examples.js.map +1 -1
  32. package/dist/esm/index.js +1186 -153
  33. package/dist/esm/index.js.map +1 -1
  34. package/dist/esm/types/api/createDiagramEditor.d.ts +19 -1
  35. package/dist/esm/types/api/index.d.ts +1 -1
  36. package/dist/esm/types/api/types.d.ts +41 -0
  37. package/dist/esm/types/displaybox/DisplayBoxControls.d.ts +5 -1
  38. package/dist/esm/types/displaybox/demos/AsymmetricPortMultiAnchorDemoTab.d.ts +3 -0
  39. package/dist/esm/types/displaybox/demos/LayoutLabelReservedSpaceDemoTab.d.ts +3 -0
  40. package/dist/esm/types/displaybox/demos/VertexControlLinkSessionDemoTab.d.ts +3 -0
  41. package/dist/esm/types/displaybox/demos/asymmetricPortMultiAnchorDemo.d.ts +31 -0
  42. package/dist/esm/types/displaybox/demos/labelStyleDemo.d.ts +2 -0
  43. package/dist/esm/types/displaybox/demos/layoutLabelReservedSpaceDemo.d.ts +11 -0
  44. package/dist/esm/types/displaybox/demos/portPositionLimitsDemo.d.ts +2 -0
  45. package/dist/esm/types/displaybox/demos/vertexControlLinkSessionDemo.d.ts +12 -0
  46. package/dist/esm/types/displaybox/useDemoControls.d.ts +4 -0
  47. package/dist/esm/types/engine/AutoLayoutService.d.ts +2 -0
  48. package/dist/esm/types/engine/DiagramEngine.d.ts +11 -0
  49. package/dist/esm/types/engine/LinkRoutingService.d.ts +9 -1
  50. package/dist/esm/types/models/PortModel.d.ts +5 -0
  51. package/dist/esm/types/renderer/RenderTypes.d.ts +3 -1
  52. package/dist/esm/types/renderer/konva/KonvaInteraction.d.ts +14 -0
  53. package/dist/esm/types/renderer/konva/KonvaNodeFactory.d.ts +12 -0
  54. package/dist/esm/types/renderer/konva/KonvaRenderer.d.ts +2 -1
  55. package/dist/esm/types/shapes/BuiltInShapes.d.ts +3 -1
  56. package/dist/esm/types/strategies/ObstacleRouter.d.ts +2 -0
  57. package/dist/esm/types/utils/__tests__/portGeometry.test.d.ts +1 -0
  58. package/dist/esm/types/utils/portGeometry.d.ts +44 -0
  59. package/dist/examples.d.ts +59 -0
  60. package/dist/index.d.ts +67 -1
  61. package/package.json +2 -1
  62. package/src/displaybox/demos/AsymmetricPortMultiAnchorDemoTab.tsx +269 -0
  63. package/src/displaybox/demos/AutoLayoutDemoTab.tsx +113 -11
  64. package/src/displaybox/demos/DeletionEventsDemoTab.tsx +6 -1
  65. package/src/displaybox/demos/EngineEventsDemoTab.tsx +5 -0
  66. package/src/displaybox/demos/EventHandlersDemoTab.tsx +5 -0
  67. package/src/displaybox/demos/ExternalDragDropDemoTab.tsx +5 -0
  68. package/src/displaybox/demos/LayoutLabelReservedSpaceDemoTab.tsx +291 -0
  69. package/src/displaybox/demos/LinkCancelDemoTab.tsx +5 -0
  70. package/src/displaybox/demos/ObstacleRoutingDemoTab.tsx +11 -10
  71. package/src/displaybox/demos/ShapeHoverControlsDemoTab.tsx +6 -1
  72. package/src/displaybox/demos/SimpleDemo.tsx +5 -0
  73. package/src/displaybox/demos/SvgPathDemoTab.tsx +5 -0
  74. package/src/displaybox/demos/TextLayoutDemoTab.tsx +6 -1
  75. package/src/displaybox/demos/VertexControlLinkSessionDemoTab.tsx +302 -0
  76. package/src/displaybox/demos/asymmetricPortMultiAnchorDemo.ts +357 -0
  77. package/src/displaybox/demos/autoLayoutDemo.ts +23 -5
  78. package/src/displaybox/demos/index.tsx +110 -80
  79. package/src/displaybox/demos/labelStyleDemo.ts +101 -0
  80. package/src/displaybox/demos/layoutLabelReservedSpaceDemo.ts +121 -0
  81. package/src/displaybox/demos/obstacleRoutingDemo.ts +212 -176
  82. package/src/displaybox/demos/portPositionLimitsDemo.ts +211 -0
  83. package/src/displaybox/demos/vertexControlLinkSessionDemo.ts +145 -0
@@ -1,176 +1,212 @@
1
- import type { DiagramState, LinkData } from '../../api';
2
- import type { DemoAction, DemoConfig } from '../types';
3
- import { baseElementShapes, basePortShapes } from './shared';
4
-
5
- const obstacleRoutingLinks: LinkData[] = [
6
- {
7
- id: 'block-link',
8
- sourcePortId: 'block-source-port',
9
- targetPortId: 'block-target-port',
10
- points: [],
11
- routing: 'auto',
12
- },
13
- {
14
- id: 'shared-sibling-link',
15
- sourcePortId: 'shared-child-a-port',
16
- targetPortId: 'shared-child-b-port',
17
- points: [],
18
- routing: 'auto',
19
- },
20
- {
21
- id: 'parent-child-link',
22
- sourcePortId: 'parent-host-port',
23
- targetPortId: 'edge-child-port',
24
- points: [],
25
- routing: 'auto',
26
- },
27
- {
28
- id: 'grandchild-link',
29
- sourcePortId: 'grandchild-a-port',
30
- targetPortId: 'grandchild-b-port',
31
- points: [],
32
- routing: 'auto',
33
- },
34
- ];
35
-
36
- const createObstacleRoutingState = (): DiagramState => ({
37
- elements: [
38
- {
39
- id: 'block-source',
40
- position: { x: 60, y: 40 },
41
- size: { width: 140, height: 90 },
42
- shapeId: 'default',
43
- },
44
- {
45
- id: 'block-target',
46
- position: { x: 380, y: 40 },
47
- size: { width: 140, height: 90 },
48
- shapeId: 'panel',
49
- },
50
- {
51
- id: 'block-obstacle',
52
- position: { x: 250, y: 30 },
53
- size: { width: 80, height: 110 },
54
- shapeId: 'default',
55
- },
56
- {
57
- id: 'shared-parent',
58
- position: { x: 40, y: 180 },
59
- size: { width: 420, height: 220 },
60
- shapeId: 'panel',
61
- style: { fill: '#fafafa' },
62
- },
63
- {
64
- id: 'shared-child-a',
65
- position: { x: 40, y: 40 },
66
- size: { width: 140, height: 90 },
67
- shapeId: 'default',
68
- parentId: 'shared-parent',
69
- },
70
- {
71
- id: 'shared-child-b',
72
- position: { x: 220, y: 40 },
73
- size: { width: 140, height: 90 },
74
- shapeId: 'default',
75
- parentId: 'shared-parent',
76
- },
77
- {
78
- id: 'parent-host',
79
- position: { x: 500, y: 180 },
80
- size: { width: 320, height: 220 },
81
- shapeId: 'panel',
82
- style: { fill: '#fafafa' },
83
- },
84
- {
85
- id: 'edge-child',
86
- position: { x: 40, y: 60 },
87
- size: { width: 160, height: 90 },
88
- shapeId: 'default',
89
- parentId: 'parent-host',
90
- },
91
- {
92
- id: 'grandparent',
93
- position: { x: 40, y: 380 },
94
- size: { width: 780, height: 160 },
95
- shapeId: 'panel',
96
- style: { fill: '#fafafa' },
97
- },
98
- {
99
- id: 'mid-parent-a',
100
- position: { x: 40, y: 30 },
101
- size: { width: 300, height: 100 },
102
- shapeId: 'panel',
103
- parentId: 'grandparent',
104
- },
105
- {
106
- id: 'mid-parent-b',
107
- position: { x: 420, y: 30 },
108
- size: { width: 300, height: 100 },
109
- shapeId: 'panel',
110
- parentId: 'grandparent',
111
- },
112
- {
113
- id: 'grandchild-a',
114
- position: { x: 40, y: 20 },
115
- size: { width: 120, height: 60 },
116
- shapeId: 'default',
117
- parentId: 'mid-parent-a',
118
- },
119
- {
120
- id: 'grandchild-b',
121
- position: { x: 40, y: 20 },
122
- size: { width: 120, height: 60 },
123
- shapeId: 'default',
124
- parentId: 'mid-parent-b',
125
- },
126
- ],
127
- ports: [
128
- { id: 'block-source-port', elementId: 'block-source', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
129
- { id: 'block-target-port', elementId: 'block-target', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
130
- { id: 'shared-child-a-port', elementId: 'shared-child-a', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
131
- { id: 'shared-child-b-port', elementId: 'shared-child-b', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
132
- { id: 'parent-host-port', elementId: 'parent-host', position: { x: 250, y: 110 }, shapeId: 'port-circle' },
133
- { id: 'edge-child-port', elementId: 'edge-child', position: { x: 0, y: 45 }, shapeId: 'port-circle' },
134
- { id: 'grandchild-a-port', elementId: 'grandchild-a', position: { x: 60, y: 30 }, shapeId: 'port-circle' },
135
- { id: 'grandchild-b-port', elementId: 'grandchild-b', position: { x: 60, y: 30 }, shapeId: 'port-circle' },
136
- ],
137
- links: obstacleRoutingLinks,
138
- texts: [
139
- { id: 'block-label', content: 'Blocking obstacle', position: { x: 60, y: 16 } },
140
- { id: 'shared-label', content: 'Shared parent (siblings)', position: { x: 40, y: 158 } },
141
- { id: 'parent-label', content: 'Parent-child (stay inside)', position: { x: 500, y: 158 } },
142
- { id: 'grand-label', content: 'Grandchildren (shared ancestors)', position: { x: 40, y: 358 } },
143
- ],
144
- });
145
-
146
- const addLinksAction: DemoAction = {
147
- id: 'add-links',
148
- label: 'Add Links (Auto)',
149
- run: (editor, state) => {
150
- const existing = new Set(state.links.map((link) => link.id));
151
- obstacleRoutingLinks.forEach((link) => {
152
- if (existing.has(link.id)) return;
153
- editor.addLink({ ...link });
154
- });
155
- },
156
- };
157
-
158
- const rerouteAllAction: DemoAction = {
159
- id: 'reroute-links',
160
- label: 'Reroute All Links',
161
- run: (editor) => {
162
- editor.rerouteAllLinks();
163
- },
164
- };
165
-
166
- export const obstacleRoutingDemoConfig: DemoConfig = ({
167
- id: 'obstacle-routing',
168
- title: 'Obstacle Routing',
169
- description: 'Auto routing avoids obstacles and respects ancestor/edge rules.',
170
- createState: createObstacleRoutingState,
171
- elementShapes: baseElementShapes,
172
- portShapes: basePortShapes,
173
- defaultElementShapeId: 'default',
174
- defaultPortShapeId: 'port-circle',
175
- actions: [addLinksAction, rerouteAllAction],
176
- });
1
+ import type { DiagramState, LinkData, SimpleShape } from '../../api';
2
+ import type { DemoAction, DemoConfig } from '../types';
3
+ import { baseElementShapes, basePortShapes } from './shared';
4
+
5
+ const obstacleRoutingLinks: LinkData[] = [
6
+ {
7
+ id: 'sibling-external-link',
8
+ sourcePortId: 'sibling-a-port',
9
+ targetPortId: 'sibling-b-port',
10
+ points: [],
11
+ routing: 'auto',
12
+ },
13
+ {
14
+ id: 'parent-to-child-link',
15
+ sourcePortId: 'attach-parent-port',
16
+ targetPortId: 'attach-child-a-port',
17
+ points: [],
18
+ routing: 'auto',
19
+ },
20
+ {
21
+ id: 'child-to-parent-link',
22
+ sourcePortId: 'attach-child-b-port',
23
+ targetPortId: 'attach-parent-port',
24
+ points: [],
25
+ routing: 'auto',
26
+ },
27
+ {
28
+ id: 'child-to-child-link',
29
+ sourcePortId: 'attach-child-a-port',
30
+ targetPortId: 'attach-child-b-port',
31
+ points: [],
32
+ routing: 'auto',
33
+ },
34
+ ];
35
+
36
+ const obstacleRoutingShapes: SimpleShape[] = [
37
+ ...baseElementShapes,
38
+ {
39
+ id: 'routing-ellipse',
40
+ kind: 'ellipse',
41
+ style: {
42
+ fill: '#e8f8ff',
43
+ stroke: '#1e6b8f',
44
+ strokeWidth: 2,
45
+ },
46
+ },
47
+ ];
48
+
49
+ const createObstacleRoutingState = (): DiagramState => ({
50
+ elements: [
51
+ {
52
+ id: 'sibling-parent',
53
+ position: { x: 40, y: 90 },
54
+ size: { width: 540, height: 250 },
55
+ shapeId: 'panel',
56
+ style: { fill: '#fafafa' },
57
+ },
58
+ {
59
+ id: 'sibling-a',
60
+ position: { x: 70, y: 75 },
61
+ size: { width: 140, height: 100 },
62
+ shapeId: 'routing-ellipse',
63
+ parentId: 'sibling-parent',
64
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
65
+ },
66
+ {
67
+ id: 'sibling-b',
68
+ position: { x: 310, y: 75 },
69
+ size: { width: 140, height: 100 },
70
+ shapeId: 'routing-ellipse',
71
+ parentId: 'sibling-parent',
72
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
73
+ },
74
+ {
75
+ id: 'attach-parent',
76
+ position: { x: 620, y: 90 },
77
+ size: { width: 360, height: 250 },
78
+ shapeId: 'routing-ellipse',
79
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
80
+ },
81
+ {
82
+ id: 'attach-child',
83
+ position: { x: 40, y: 45 },
84
+ size: { width: 140, height: 100 },
85
+ shapeId: 'routing-ellipse',
86
+ parentId: 'attach-parent',
87
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
88
+ },
89
+ {
90
+ id: 'attach-child-b',
91
+ position: { x: 190, y: 120 },
92
+ size: { width: 140, height: 100 },
93
+ shapeId: 'routing-ellipse',
94
+ parentId: 'attach-parent',
95
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
96
+ },
97
+ ],
98
+ ports: [
99
+ {
100
+ id: 'sibling-a-port',
101
+ elementId: 'sibling-a',
102
+ position: { x: 0, y: 50 },
103
+ shapeId: 'port-dark',
104
+ anchorCenter: true,
105
+ currentAnchorId: 'left',
106
+ },
107
+ {
108
+ id: 'sibling-b-port',
109
+ elementId: 'sibling-b',
110
+ position: { x: 140, y: 50 },
111
+ shapeId: 'port-dark',
112
+ anchorCenter: true,
113
+ currentAnchorId: 'right',
114
+ },
115
+ {
116
+ id: 'attach-parent-port',
117
+ elementId: 'attach-parent',
118
+ position: { x: 20, y: 125 },
119
+ shapeId: 'port-circle',
120
+ anchorCenter: true,
121
+ currentAnchorId: 'left',
122
+ externalLinkAttachPoint: { x: -16, y: 0 },
123
+ internalLinkAttachPoint: { x: 16, y: 0 },
124
+ },
125
+ {
126
+ id: 'attach-child-a-port',
127
+ elementId: 'attach-child',
128
+ position: { x: 140, y: 50 },
129
+ shapeId: 'port-circle',
130
+ anchorCenter: true,
131
+ currentAnchorId: 'right',
132
+ externalLinkAttachPoint: { x: 12, y: 0 },
133
+ internalLinkAttachPoint: { x: -12, y: 0 },
134
+ },
135
+ {
136
+ id: 'attach-child-b-port',
137
+ elementId: 'attach-child-b',
138
+ position: { x: 0, y: 50 },
139
+ shapeId: 'port-circle',
140
+ anchorCenter: true,
141
+ currentAnchorId: 'left',
142
+ externalLinkAttachPoint: { x: 12, y: 0 },
143
+ internalLinkAttachPoint: { x: -12, y: 0 },
144
+ },
145
+ ],
146
+ links: obstacleRoutingLinks,
147
+ texts: [
148
+ {
149
+ id: 'obstacle-routing-instructions',
150
+ content:
151
+ 'Two checks in one canvas: (A) sibling external-anchor link avoids both child interiors; (B) parent/children hierarchy resolves internal vs external attach points per endpoint direction.',
152
+ position: { x: 40, y: 30 },
153
+ },
154
+ {
155
+ id: 'sibling-group-label',
156
+ content: 'Scenario A: Same-parent children, external-facing anchors (left/right)',
157
+ position: { x: 52, y: 66 },
158
+ },
159
+ {
160
+ id: 'attach-group-label',
161
+ content: 'Scenario B: Directional attach semantics (parent internal, child/sibling external)',
162
+ position: { x: 620, y: 66 },
163
+ },
164
+ {
165
+ id: 'attach-link-parent-child-label',
166
+ content: 'parent->child-A: parent endpoint INTERNAL, child endpoint EXTERNAL',
167
+ position: { x: 620, y: 350 },
168
+ },
169
+ {
170
+ id: 'attach-link-child-parent-label',
171
+ content: 'child-B->parent: child endpoint EXTERNAL, parent endpoint INTERNAL',
172
+ position: { x: 620, y: 372 },
173
+ },
174
+ {
175
+ id: 'attach-link-child-child-label',
176
+ content: 'child-A->child-B: sibling endpoints EXTERNAL on both ends',
177
+ position: { x: 620, y: 394 },
178
+ },
179
+ ],
180
+ });
181
+
182
+ const addLinksAction: DemoAction = {
183
+ id: 'add-links',
184
+ label: 'Add Links (Auto)',
185
+ run: (editor, state) => {
186
+ const existing = new Set(state.links.map((link) => link.id));
187
+ obstacleRoutingLinks.forEach((link) => {
188
+ if (existing.has(link.id)) return;
189
+ editor.addLink({ ...link });
190
+ });
191
+ },
192
+ };
193
+
194
+ const rerouteAllAction: DemoAction = {
195
+ id: 'reroute-links',
196
+ label: 'Reroute All Links',
197
+ run: (editor) => {
198
+ editor.rerouteAllLinks();
199
+ },
200
+ };
201
+
202
+ export const obstacleRoutingDemoConfig: DemoConfig = {
203
+ id: 'obstacle-routing',
204
+ title: 'Obstacle Routing',
205
+ description: 'Nested multi-anchor checks: sibling interior-avoidance + directional internal/external parent-child attach semantics.',
206
+ createState: createObstacleRoutingState,
207
+ elementShapes: obstacleRoutingShapes,
208
+ portShapes: basePortShapes,
209
+ defaultElementShapeId: 'default',
210
+ defaultPortShapeId: 'port-circle',
211
+ actions: [addLinksAction, rerouteAllAction],
212
+ };
@@ -0,0 +1,211 @@
1
+ import type { DiagramState } from '../../api';
2
+ import type { DemoConfig } from '../types';
3
+ import { baseElementShapes, basePortShapes } from './shared';
4
+
5
+ const createPortPositionLimitsState = (): DiagramState => ({
6
+ elements: [
7
+ {
8
+ id: 'limit-left-host',
9
+ position: { x: 60, y: 120 },
10
+ size: { width: 220, height: 140 },
11
+ shapeId: 'panel',
12
+ portMovement: {
13
+ moveMode: 'inside',
14
+ positionLimits: {
15
+ x: { max: 70 },
16
+ },
17
+ },
18
+ },
19
+ {
20
+ id: 'limit-right-host',
21
+ position: { x: 330, y: 120 },
22
+ size: { width: 220, height: 140 },
23
+ shapeId: 'panel',
24
+ portMovement: {
25
+ moveMode: 'inside',
26
+ positionLimits: {
27
+ x: { min: 150 },
28
+ },
29
+ },
30
+ },
31
+ {
32
+ id: 'limit-top-host',
33
+ position: { x: 600, y: 120 },
34
+ size: { width: 220, height: 140 },
35
+ shapeId: 'panel',
36
+ portMovement: {
37
+ moveMode: 'inside',
38
+ positionLimits: {
39
+ y: { max: 45 },
40
+ },
41
+ },
42
+ },
43
+ {
44
+ id: 'limit-bottom-host',
45
+ position: { x: 870, y: 120 },
46
+ size: { width: 220, height: 140 },
47
+ shapeId: 'panel',
48
+ portMovement: {
49
+ moveMode: 'inside',
50
+ positionLimits: {
51
+ y: { min: 90 },
52
+ },
53
+ },
54
+ },
55
+ {
56
+ id: 'border-left-host',
57
+ position: { x: 200, y: 350 },
58
+ size: { width: 220, height: 140 },
59
+ shapeId: 'panel',
60
+ portMovement: {
61
+ moveMode: 'border',
62
+ positionLimits: {
63
+ x: { max: 80 },
64
+ },
65
+ },
66
+ },
67
+ {
68
+ id: 'border-right-host',
69
+ position: { x: 500, y: 350 },
70
+ size: { width: 220, height: 140 },
71
+ shapeId: 'panel',
72
+ portMovement: {
73
+ moveMode: 'border',
74
+ positionLimits: {
75
+ x: { min: 140 },
76
+ },
77
+ },
78
+ },
79
+ {
80
+ id: 'default-normalize-host',
81
+ position: { x: 800, y: 350 },
82
+ size: { width: 220, height: 140 },
83
+ shapeId: 'panel',
84
+ portMovement: {
85
+ moveMode: 'inside',
86
+ positionLimits: {
87
+ x: { max: 60 },
88
+ y: { max: 60 },
89
+ },
90
+ },
91
+ },
92
+ ],
93
+ ports: [
94
+ { id: 'limit-left-port', elementId: 'limit-left-host', position: { x: 40, y: 70 }, shapeId: 'port-circle', anchorCenter: true },
95
+ { id: 'limit-right-port', elementId: 'limit-right-host', position: { x: 180, y: 70 }, shapeId: 'port-circle', anchorCenter: true },
96
+ { id: 'limit-top-port', elementId: 'limit-top-host', position: { x: 110, y: 30 }, shapeId: 'port-circle', anchorCenter: true },
97
+ { id: 'limit-bottom-port', elementId: 'limit-bottom-host', position: { x: 110, y: 110 }, shapeId: 'port-circle', anchorCenter: true },
98
+ { id: 'border-left-port', elementId: 'border-left-host', position: { x: 80, y: 0 }, shapeId: 'port-circle', anchorCenter: true },
99
+ { id: 'border-right-port', elementId: 'border-right-host', position: { x: 140, y: 140 }, shapeId: 'port-circle', anchorCenter: true },
100
+ { id: 'reload-default-port', elementId: 'default-normalize-host', position: { x: 190, y: 130 }, shapeId: 'port-circle', anchorCenter: true },
101
+ ],
102
+ links: [],
103
+ texts: [
104
+ {
105
+ id: 'limit-intro',
106
+ content:
107
+ 'Top row: threshold/limit hosts. Bottom row: border+limits hosts and default-position normalization checks for add/load flows.',
108
+ position: { x: 60, y: 40 },
109
+ },
110
+ { id: 'limit-group-title', content: 'Threshold/limit-focused hosts', position: { x: 60, y: 85 } },
111
+ { id: 'border-group-title', content: 'Border+limits + default-position normalization', position: { x: 200, y: 315 } },
112
+ { id: 'limit-left-title', content: 'x.max = 70 (left of X)', position: { x: 8, y: -20 }, ownerId: 'limit-left-host' },
113
+ { id: 'limit-right-title', content: 'x.min = 150 (right of X)', position: { x: 8, y: -20 }, ownerId: 'limit-right-host' },
114
+ { id: 'limit-top-title', content: 'y.max = 45 (above Y)', position: { x: 8, y: -20 }, ownerId: 'limit-top-host' },
115
+ { id: 'limit-bottom-title', content: 'y.min = 90 (below Y)', position: { x: 8, y: -20 }, ownerId: 'limit-bottom-host' },
116
+ { id: 'border-left-title', content: 'moveMode=border + x.max = 80', position: { x: 8, y: -20 }, ownerId: 'border-left-host' },
117
+ { id: 'border-right-title', content: 'moveMode=border + x.min = 140', position: { x: 8, y: -20 }, ownerId: 'border-right-host' },
118
+ { id: 'default-normalize-title', content: 'default normalization host x/y.max = 60', position: { x: 8, y: -20 }, ownerId: 'default-normalize-host' },
119
+ { id: 'limit-left-status', content: 'local: x=40, y=70', position: { x: 8, y: 110 }, ownerId: 'limit-left-host' },
120
+ { id: 'limit-right-status', content: 'local: x=180, y=70', position: { x: 8, y: 110 }, ownerId: 'limit-right-host' },
121
+ { id: 'limit-top-status', content: 'local: x=110, y=30', position: { x: 8, y: 110 }, ownerId: 'limit-top-host' },
122
+ { id: 'limit-bottom-status', content: 'local: x=110, y=110', position: { x: 8, y: 110 }, ownerId: 'limit-bottom-host' },
123
+ { id: 'border-left-status', content: 'local: x=80, y=0', position: { x: 8, y: 110 }, ownerId: 'border-left-host' },
124
+ { id: 'border-right-status', content: 'local: x=140, y=140', position: { x: 8, y: 110 }, ownerId: 'border-right-host' },
125
+ {
126
+ id: 'add-default-status',
127
+ content: 'add invalid default: not executed',
128
+ position: { x: 8, y: 88 },
129
+ ownerId: 'default-normalize-host',
130
+ },
131
+ {
132
+ id: 'reload-default-status',
133
+ content: 'reload invalid default: click "Read reload-normalized port"',
134
+ position: { x: 8, y: 106 },
135
+ ownerId: 'default-normalize-host',
136
+ },
137
+ ],
138
+ });
139
+
140
+ export const portPositionLimitsDemoConfig: DemoConfig = {
141
+ id: 'port-position-limits',
142
+ title: 'Port Position Limits',
143
+ description: 'Unified restriction demo: threshold limits, border+limits, and add/load default-position normalization.',
144
+ createState: createPortPositionLimitsState,
145
+ elementShapes: baseElementShapes,
146
+ portShapes: basePortShapes,
147
+ defaultElementShapeId: 'default',
148
+ defaultPortShapeId: 'port-circle',
149
+ actions: [
150
+ {
151
+ id: 'force-limit-clamp',
152
+ label: 'Force out-of-range moves',
153
+ run: (editor) => {
154
+ editor.movePortTo('limit-left-port', 1000, 1000);
155
+ editor.movePortTo('limit-right-port', -1000, 1000);
156
+ editor.movePortTo('limit-top-port', 1000, 1000);
157
+ editor.movePortTo('limit-bottom-port', 1000, -1000);
158
+ editor.movePortTo('border-left-port', 1000, 1000);
159
+ editor.movePortTo('border-right-port', -1000, -1000);
160
+
161
+ const state = editor.getState();
162
+ const left = state.ports.find((item) => item.id === 'limit-left-port');
163
+ const right = state.ports.find((item) => item.id === 'limit-right-port');
164
+ const top = state.ports.find((item) => item.id === 'limit-top-port');
165
+ const bottom = state.ports.find((item) => item.id === 'limit-bottom-port');
166
+ const borderLeft = state.ports.find((item) => item.id === 'border-left-port');
167
+ const borderRight = state.ports.find((item) => item.id === 'border-right-port');
168
+
169
+ if (left) editor.updateText('limit-left-status', `local: x=${left.position.x}, y=${left.position.y}`);
170
+ if (right) editor.updateText('limit-right-status', `local: x=${right.position.x}, y=${right.position.y}`);
171
+ if (top) editor.updateText('limit-top-status', `local: x=${top.position.x}, y=${top.position.y}`);
172
+ if (bottom) editor.updateText('limit-bottom-status', `local: x=${bottom.position.x}, y=${bottom.position.y}`);
173
+ if (borderLeft) editor.updateText('border-left-status', `local: x=${borderLeft.position.x}, y=${borderLeft.position.y}`);
174
+ if (borderRight) editor.updateText('border-right-status', `local: x=${borderRight.position.x}, y=${borderRight.position.y}`);
175
+ },
176
+ },
177
+ {
178
+ id: 'add-invalid-default-port',
179
+ label: 'Add invalid default port',
180
+ run: (editor) => {
181
+ editor.removePort('added-default-port');
182
+ editor.addPortToElement('default-normalize-host', {
183
+ id: 'added-default-port',
184
+ elementId: 'default-normalize-host',
185
+ position: { x: 180, y: 120 },
186
+ shapeId: 'port-circle',
187
+ anchorCenter: true,
188
+ });
189
+ const state = editor.getState();
190
+ const added = state.ports.find((item) => item.id === 'added-default-port');
191
+ if (added) {
192
+ editor.updateText('add-default-status', `add invalid default: local x=${added.position.x}, y=${added.position.y}`);
193
+ }
194
+ },
195
+ },
196
+ {
197
+ id: 'read-reload-normalized-port',
198
+ label: 'Read reload-normalized port',
199
+ run: (editor) => {
200
+ const state = editor.getState();
201
+ const reloaded = state.ports.find((item) => item.id === 'reload-default-port');
202
+ if (reloaded) {
203
+ editor.updateText(
204
+ 'reload-default-status',
205
+ `reload invalid default: local x=${reloaded.position.x}, y=${reloaded.position.y}`,
206
+ );
207
+ }
208
+ },
209
+ },
210
+ ],
211
+ };