orcasvn-react-diagrams 0.2.4 → 0.2.6
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/CHANGELOG.md +42 -4
- package/README.md +15 -32
- package/ai/api-contract.json +88 -3
- package/ai/manifest.json +1 -1
- package/dist/cjs/examples.js +6519 -4423
- package/dist/cjs/index.js +1513 -120
- package/dist/cjs/types/api/createDiagramEditor.d.ts +4 -1
- package/dist/cjs/types/api/types.d.ts +43 -1
- package/dist/cjs/types/displaybox/demos/LinkColorPoolDemoTab.d.ts +3 -0
- package/dist/cjs/types/displaybox/demos/OffsetAnchorAvoidanceDemoTab.d.ts +3 -0
- package/dist/cjs/types/displaybox/demos/SimpleDemo.d.ts +4 -1
- package/dist/cjs/types/displaybox/demos/ZoomToFitElementsDemoTab.d.ts +3 -0
- package/dist/cjs/types/displaybox/demos/linkColorPoolDemo.d.ts +2 -0
- package/dist/cjs/types/displaybox/demos/offsetAnchorAvoidanceDemo.d.ts +4 -0
- package/dist/cjs/types/displaybox/demos/textDemo.d.ts +3 -0
- package/dist/cjs/types/displaybox/demos/zoomToFitElementsDemo.d.ts +4 -0
- package/dist/cjs/types/displaybox/types.d.ts +3 -1
- package/dist/cjs/types/displaybox/useDemoEditor.d.ts +4 -2
- package/dist/cjs/types/engine/AutoLayoutService.d.ts +59 -2
- package/dist/cjs/types/engine/DiagramEngine.d.ts +20 -1
- package/dist/cjs/types/engine/LinkRoutingService.d.ts +9 -0
- package/dist/cjs/types/models/TextModel.d.ts +1 -0
- package/dist/cjs/types/renderer/konva/KonvaInteraction.d.ts +6 -0
- package/dist/cjs/types/strategies/ObstacleRouter.d.ts +2 -0
- package/dist/cjs/types/strategies/RouterStrategy.d.ts +10 -0
- package/dist/esm/examples.js +6519 -4423
- package/dist/esm/examples.js.map +1 -1
- package/dist/esm/index.js +1513 -120
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/api/createDiagramEditor.d.ts +4 -1
- package/dist/esm/types/api/types.d.ts +43 -1
- package/dist/esm/types/displaybox/demos/LinkColorPoolDemoTab.d.ts +3 -0
- package/dist/esm/types/displaybox/demos/OffsetAnchorAvoidanceDemoTab.d.ts +3 -0
- package/dist/esm/types/displaybox/demos/SimpleDemo.d.ts +4 -1
- package/dist/esm/types/displaybox/demos/ZoomToFitElementsDemoTab.d.ts +3 -0
- package/dist/esm/types/displaybox/demos/linkColorPoolDemo.d.ts +2 -0
- package/dist/esm/types/displaybox/demos/offsetAnchorAvoidanceDemo.d.ts +4 -0
- package/dist/esm/types/displaybox/demos/textDemo.d.ts +3 -0
- package/dist/esm/types/displaybox/demos/zoomToFitElementsDemo.d.ts +4 -0
- package/dist/esm/types/displaybox/types.d.ts +3 -1
- package/dist/esm/types/displaybox/useDemoEditor.d.ts +4 -2
- package/dist/esm/types/engine/AutoLayoutService.d.ts +59 -2
- package/dist/esm/types/engine/DiagramEngine.d.ts +20 -1
- package/dist/esm/types/engine/LinkRoutingService.d.ts +9 -0
- package/dist/esm/types/models/TextModel.d.ts +1 -0
- package/dist/esm/types/renderer/konva/KonvaInteraction.d.ts +6 -0
- package/dist/esm/types/strategies/ObstacleRouter.d.ts +2 -0
- package/dist/esm/types/strategies/RouterStrategy.d.ts +10 -0
- package/dist/examples.d.ts +56 -1
- package/dist/index.d.ts +60 -2
- package/docs/API_CONTRACT.md +73 -3
- package/docs/CAPABILITIES.md +8 -0
- package/docs/COMMANDS_EVENTS.md +8 -0
- package/docs/DOCUMENTATION_WORKFLOW.md +4 -1
- package/docs/INTEGRATION_PLAYBOOK.md +3 -0
- package/docs/STATE_INVARIANTS.md +4 -0
- package/package.json +1 -1
- package/src/displaybox/demos/AutoLayoutDemoTab.tsx +515 -381
- package/src/displaybox/demos/EngineEventsDemoTab.tsx +12 -11
- package/src/displaybox/demos/LinkColorPoolDemoTab.tsx +49 -0
- package/src/displaybox/demos/ObstacleRoutingDemoTab.tsx +52 -31
- package/src/displaybox/demos/OffsetAnchorAvoidanceDemoTab.tsx +30 -0
- package/src/displaybox/demos/SimpleDemo.tsx +18 -13
- package/src/displaybox/demos/TextLayoutDemoTab.tsx +18 -1
- package/src/displaybox/demos/ZoomToFitElementsDemoTab.tsx +29 -0
- package/src/displaybox/demos/autoLayoutDemo.ts +62 -26
- package/src/displaybox/demos/index.tsx +81 -57
- package/src/displaybox/demos/linkColorPoolDemo.ts +122 -0
- package/src/displaybox/demos/obstacleRoutingDemo.ts +77 -1
- package/src/displaybox/demos/offsetAnchorAvoidanceDemo.ts +211 -0
- package/src/displaybox/demos/routingDemo.ts +5 -5
- package/src/displaybox/demos/shared.ts +17 -12
- package/src/displaybox/demos/textDemo.ts +6 -2
- package/src/displaybox/demos/zoomToFitElementsDemo.ts +83 -0
- package/src/displaybox/types.ts +10 -8
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { DiagramState } from '../../api';
|
|
2
|
+
import { createId } from '../../utils/ids';
|
|
3
|
+
import type { DemoConfig } from '../types';
|
|
4
|
+
import { baseElementShapes, basePortShapes } from './shared';
|
|
5
|
+
|
|
6
|
+
const createLinkColorPoolState = (): DiagramState => ({
|
|
7
|
+
elements: [
|
|
8
|
+
{
|
|
9
|
+
id: 'lc-off-source',
|
|
10
|
+
position: { x: 80, y: 170 },
|
|
11
|
+
size: { width: 190, height: 120 },
|
|
12
|
+
shapeId: 'default',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: 'lc-off-target',
|
|
16
|
+
position: { x: 350, y: 170 },
|
|
17
|
+
size: { width: 190, height: 120 },
|
|
18
|
+
shapeId: 'default',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'lc-default-source',
|
|
22
|
+
position: { x: 80, y: 360 },
|
|
23
|
+
size: { width: 190, height: 120 },
|
|
24
|
+
shapeId: 'panel',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'lc-default-target',
|
|
28
|
+
position: { x: 350, y: 360 },
|
|
29
|
+
size: { width: 190, height: 120 },
|
|
30
|
+
shapeId: 'panel',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'lc-custom-source',
|
|
34
|
+
position: { x: 620, y: 170 },
|
|
35
|
+
size: { width: 190, height: 120 },
|
|
36
|
+
shapeId: 'default',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'lc-custom-target',
|
|
40
|
+
position: { x: 620, y: 360 },
|
|
41
|
+
size: { width: 190, height: 120 },
|
|
42
|
+
shapeId: 'default',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
ports: [
|
|
46
|
+
{ id: 'lc-off-source-port', elementId: 'lc-off-source', position: { x: 170, y: 60 }, shapeId: 'port-circle' },
|
|
47
|
+
{ id: 'lc-off-target-port', elementId: 'lc-off-target', position: { x: 20, y: 60 }, shapeId: 'port-circle' },
|
|
48
|
+
{ id: 'lc-default-source-port', elementId: 'lc-default-source', position: { x: 170, y: 60 }, shapeId: 'port-circle' },
|
|
49
|
+
{ id: 'lc-default-target-port', elementId: 'lc-default-target', position: { x: 20, y: 60 }, shapeId: 'port-circle' },
|
|
50
|
+
{ id: 'lc-custom-source-port', elementId: 'lc-custom-source', position: { x: 95, y: 110 }, shapeId: 'port-circle' },
|
|
51
|
+
{ id: 'lc-custom-target-port', elementId: 'lc-custom-target', position: { x: 95, y: 10 }, shapeId: 'port-circle' },
|
|
52
|
+
],
|
|
53
|
+
links: [
|
|
54
|
+
{
|
|
55
|
+
id: 'lc-reference-link',
|
|
56
|
+
sourcePortId: 'lc-off-source-port',
|
|
57
|
+
targetPortId: 'lc-off-target-port',
|
|
58
|
+
points: [],
|
|
59
|
+
style: { stroke: '#374151', strokeWidth: 2 },
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
texts: [
|
|
63
|
+
{
|
|
64
|
+
id: 'lc-intro',
|
|
65
|
+
content:
|
|
66
|
+
'Use Color Mode above: Off keeps baseline link color behavior. Default Pool enables random colors from the built-in 20-color pool. Custom Pool restricts random colors to #0b7285, #e8590c, #5f3dc4.',
|
|
67
|
+
position: { x: 80, y: 86 },
|
|
68
|
+
},
|
|
69
|
+
{ id: 'lc-off-label', content: 'Off scenario pair', ownerId: 'lc-off-source', position: { x: 10, y: -16 } },
|
|
70
|
+
{ id: 'lc-default-label', content: 'Default-pool scenario pair', ownerId: 'lc-default-source', position: { x: 10, y: -16 } },
|
|
71
|
+
{ id: 'lc-custom-label', content: 'Custom-pool scenario pair', ownerId: 'lc-custom-source', position: { x: 10, y: -16 } },
|
|
72
|
+
],
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export const linkColorPoolDemoConfig: DemoConfig = {
|
|
76
|
+
id: 'link-color-pool',
|
|
77
|
+
title: 'Link Color Pool',
|
|
78
|
+
description: 'Verify disabled/default/custom random link-color assignment on new links.',
|
|
79
|
+
createState: createLinkColorPoolState,
|
|
80
|
+
elementShapes: baseElementShapes,
|
|
81
|
+
portShapes: basePortShapes,
|
|
82
|
+
defaultElementShapeId: 'default',
|
|
83
|
+
defaultPortShapeId: 'port-circle',
|
|
84
|
+
actions: [
|
|
85
|
+
{
|
|
86
|
+
id: 'add-link-off-pair',
|
|
87
|
+
label: 'Add link (off pair)',
|
|
88
|
+
run: (editor) => {
|
|
89
|
+
editor.addLink({
|
|
90
|
+
id: `link-color-off-${createId()}`,
|
|
91
|
+
sourcePortId: 'lc-off-source-port',
|
|
92
|
+
targetPortId: 'lc-off-target-port',
|
|
93
|
+
points: [],
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: 'add-link-default-pair',
|
|
99
|
+
label: 'Add link (default pair)',
|
|
100
|
+
run: (editor) => {
|
|
101
|
+
editor.addLink({
|
|
102
|
+
id: `link-color-default-${createId()}`,
|
|
103
|
+
sourcePortId: 'lc-default-source-port',
|
|
104
|
+
targetPortId: 'lc-default-target-port',
|
|
105
|
+
points: [],
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: 'add-link-custom-pair',
|
|
111
|
+
label: 'Add link (custom pair)',
|
|
112
|
+
run: (editor) => {
|
|
113
|
+
editor.addLink({
|
|
114
|
+
id: `link-color-custom-${createId()}`,
|
|
115
|
+
sourcePortId: 'lc-custom-source-port',
|
|
116
|
+
targetPortId: 'lc-custom-target-port',
|
|
117
|
+
points: [],
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
};
|
|
@@ -31,6 +31,17 @@ const obstacleRoutingLinks: LinkData[] = [
|
|
|
31
31
|
points: [],
|
|
32
32
|
routing: 'auto',
|
|
33
33
|
},
|
|
34
|
+
{
|
|
35
|
+
id: 'cross-branch-corridor-link',
|
|
36
|
+
sourcePortId: 'corridor-left-port',
|
|
37
|
+
targetPortId: 'corridor-right-port',
|
|
38
|
+
routing: 'auto',
|
|
39
|
+
points: [
|
|
40
|
+
{ x: 310, y: 560 },
|
|
41
|
+
{ x: 470, y: 500 },
|
|
42
|
+
{ x: 630, y: 560 },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
34
45
|
];
|
|
35
46
|
|
|
36
47
|
const obstacleRoutingShapes: SimpleShape[] = [
|
|
@@ -94,6 +105,45 @@ const createObstacleRoutingState = (): DiagramState => ({
|
|
|
94
105
|
parentId: 'attach-parent',
|
|
95
106
|
portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
|
|
96
107
|
},
|
|
108
|
+
{
|
|
109
|
+
id: 'corridor-root',
|
|
110
|
+
position: { x: 40, y: 390 },
|
|
111
|
+
size: { width: 940, height: 260 },
|
|
112
|
+
shapeId: 'panel',
|
|
113
|
+
style: { fill: '#f5f9ff' },
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'corridor-left-branch',
|
|
117
|
+
position: { x: 80, y: 40 },
|
|
118
|
+
size: { width: 260, height: 170 },
|
|
119
|
+
shapeId: 'routing-ellipse',
|
|
120
|
+
parentId: 'corridor-root',
|
|
121
|
+
portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: 'corridor-right-branch',
|
|
125
|
+
position: { x: 560, y: 50 },
|
|
126
|
+
size: { width: 280, height: 170 },
|
|
127
|
+
shapeId: 'routing-ellipse',
|
|
128
|
+
parentId: 'corridor-root',
|
|
129
|
+
portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: 'corridor-left-leaf',
|
|
133
|
+
position: { x: 120, y: 60 },
|
|
134
|
+
size: { width: 120, height: 70 },
|
|
135
|
+
shapeId: 'default',
|
|
136
|
+
parentId: 'corridor-left-branch',
|
|
137
|
+
portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'corridor-right-leaf',
|
|
141
|
+
position: { x: 50, y: 70 },
|
|
142
|
+
size: { width: 120, height: 70 },
|
|
143
|
+
shapeId: 'default',
|
|
144
|
+
parentId: 'corridor-right-branch',
|
|
145
|
+
portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
|
|
146
|
+
},
|
|
97
147
|
],
|
|
98
148
|
ports: [
|
|
99
149
|
{
|
|
@@ -142,6 +192,22 @@ const createObstacleRoutingState = (): DiagramState => ({
|
|
|
142
192
|
externalLinkAttachPoint: { x: 12, y: 0 },
|
|
143
193
|
internalLinkAttachPoint: { x: -12, y: 0 },
|
|
144
194
|
},
|
|
195
|
+
{
|
|
196
|
+
id: 'corridor-left-port',
|
|
197
|
+
elementId: 'corridor-left-leaf',
|
|
198
|
+
position: { x: 120, y: 35 },
|
|
199
|
+
shapeId: 'port-dark',
|
|
200
|
+
anchorCenter: true,
|
|
201
|
+
currentAnchorId: 'right',
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: 'corridor-right-port',
|
|
205
|
+
elementId: 'corridor-right-leaf',
|
|
206
|
+
position: { x: 0, y: 35 },
|
|
207
|
+
shapeId: 'port-dark',
|
|
208
|
+
anchorCenter: true,
|
|
209
|
+
currentAnchorId: 'left',
|
|
210
|
+
},
|
|
145
211
|
],
|
|
146
212
|
links: obstacleRoutingLinks,
|
|
147
213
|
texts: [
|
|
@@ -176,6 +242,16 @@ const createObstacleRoutingState = (): DiagramState => ({
|
|
|
176
242
|
content: 'child-A->child-B: sibling endpoints EXTERNAL on both ends',
|
|
177
243
|
position: { x: 620, y: 394 },
|
|
178
244
|
},
|
|
245
|
+
{
|
|
246
|
+
id: 'corridor-group-label',
|
|
247
|
+
content: 'Scenario C: Cross-branch route should leave source branch early, travel at ancestor corridor, and descend near target.',
|
|
248
|
+
position: { x: 52, y: 360 },
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: 'corridor-redraw-label',
|
|
252
|
+
content: 'The cross-branch link starts with stale points. Toggle redraw refresh and click Manual Render to compare behavior.',
|
|
253
|
+
position: { x: 52, y: 676 },
|
|
254
|
+
},
|
|
179
255
|
],
|
|
180
256
|
});
|
|
181
257
|
|
|
@@ -202,7 +278,7 @@ const rerouteAllAction: DemoAction = {
|
|
|
202
278
|
export const obstacleRoutingDemoConfig: DemoConfig = {
|
|
203
279
|
id: 'obstacle-routing',
|
|
204
280
|
title: 'Obstacle Routing',
|
|
205
|
-
description: 'Nested
|
|
281
|
+
description: 'Nested routing checks: sibling interior-avoidance, directional parent-child attach semantics, and cross-parent common-ancestor corridor preference.',
|
|
206
282
|
createState: createObstacleRoutingState,
|
|
207
283
|
elementShapes: obstacleRoutingShapes,
|
|
208
284
|
portShapes: basePortShapes,
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import type { DiagramState, SimpleShape } from '../../api';
|
|
2
|
+
import type { DemoAction, DemoConfig } from '../types';
|
|
3
|
+
import { baseElementShapes, basePortShapes } from './shared';
|
|
4
|
+
|
|
5
|
+
const offsetAnchorShapes: SimpleShape[] = [
|
|
6
|
+
...baseElementShapes,
|
|
7
|
+
{
|
|
8
|
+
id: 'offset-anchor-ellipse',
|
|
9
|
+
kind: 'ellipse',
|
|
10
|
+
style: {
|
|
11
|
+
fill: '#eef8ff',
|
|
12
|
+
stroke: '#2563eb',
|
|
13
|
+
strokeWidth: 2,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export const createOffsetAnchorAvoidanceState = (): DiagramState => ({
|
|
19
|
+
elements: [
|
|
20
|
+
{
|
|
21
|
+
id: 'offset-sibling-parent',
|
|
22
|
+
position: { x: 40, y: 110 },
|
|
23
|
+
size: { width: 520, height: 250 },
|
|
24
|
+
shapeId: 'panel',
|
|
25
|
+
style: { fill: '#faf7ef' },
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'offset-sibling-a',
|
|
29
|
+
position: { x: 60, y: 78 },
|
|
30
|
+
size: { width: 130, height: 96 },
|
|
31
|
+
shapeId: 'offset-anchor-ellipse',
|
|
32
|
+
parentId: 'offset-sibling-parent',
|
|
33
|
+
portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'offset-sibling-b',
|
|
37
|
+
position: { x: 320, y: 78 },
|
|
38
|
+
size: { width: 130, height: 96 },
|
|
39
|
+
shapeId: 'offset-anchor-ellipse',
|
|
40
|
+
parentId: 'offset-sibling-parent',
|
|
41
|
+
portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'offset-direction-parent',
|
|
45
|
+
position: { x: 620, y: 110 },
|
|
46
|
+
size: { width: 330, height: 250 },
|
|
47
|
+
shapeId: 'offset-anchor-ellipse',
|
|
48
|
+
portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'offset-direction-child',
|
|
52
|
+
position: { x: 48, y: 74 },
|
|
53
|
+
size: { width: 128, height: 92 },
|
|
54
|
+
shapeId: 'default',
|
|
55
|
+
parentId: 'offset-direction-parent',
|
|
56
|
+
portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'offset-direction-sibling',
|
|
60
|
+
position: { x: 190, y: 126 },
|
|
61
|
+
size: { width: 96, height: 70 },
|
|
62
|
+
shapeId: 'default',
|
|
63
|
+
parentId: 'offset-direction-parent',
|
|
64
|
+
style: { fill: '#fff6e8', stroke: '#b45309', strokeWidth: 2 },
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
ports: [
|
|
68
|
+
{
|
|
69
|
+
id: 'offset-sibling-a-port',
|
|
70
|
+
elementId: 'offset-sibling-a',
|
|
71
|
+
position: { x: 0, y: 48 },
|
|
72
|
+
shapeId: 'port-dark',
|
|
73
|
+
anchorCenter: true,
|
|
74
|
+
currentAnchorId: 'left',
|
|
75
|
+
moveMode: 'border',
|
|
76
|
+
externalLinkAttachPoint: { x: 0, y: 24 },
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'offset-sibling-b-port',
|
|
80
|
+
elementId: 'offset-sibling-b',
|
|
81
|
+
position: { x: 130, y: 48 },
|
|
82
|
+
shapeId: 'port-dark',
|
|
83
|
+
anchorCenter: true,
|
|
84
|
+
currentAnchorId: 'right',
|
|
85
|
+
moveMode: 'border',
|
|
86
|
+
externalLinkAttachPoint: { x: 0, y: 24 },
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 'offset-parent-port',
|
|
90
|
+
elementId: 'offset-direction-parent',
|
|
91
|
+
position: { x: 18, y: 125 },
|
|
92
|
+
shapeId: 'port-circle',
|
|
93
|
+
anchorCenter: true,
|
|
94
|
+
currentAnchorId: 'left',
|
|
95
|
+
moveMode: 'border',
|
|
96
|
+
externalLinkAttachPoint: { x: 0, y: 18 },
|
|
97
|
+
internalLinkAttachPoint: { x: 0, y: -18 },
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: 'offset-child-port',
|
|
101
|
+
elementId: 'offset-direction-child',
|
|
102
|
+
position: { x: 128, y: 46 },
|
|
103
|
+
shapeId: 'port-circle',
|
|
104
|
+
anchorCenter: true,
|
|
105
|
+
currentAnchorId: 'right',
|
|
106
|
+
moveMode: 'border',
|
|
107
|
+
externalLinkAttachPoint: { x: 0, y: 18 },
|
|
108
|
+
internalLinkAttachPoint: { x: 0, y: -18 },
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
links: [
|
|
112
|
+
{
|
|
113
|
+
id: 'offset-sibling-link',
|
|
114
|
+
sourcePortId: 'offset-sibling-a-port',
|
|
115
|
+
targetPortId: 'offset-sibling-b-port',
|
|
116
|
+
points: [],
|
|
117
|
+
routing: 'auto',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'offset-parent-child-link',
|
|
121
|
+
sourcePortId: 'offset-parent-port',
|
|
122
|
+
targetPortId: 'offset-child-port',
|
|
123
|
+
points: [],
|
|
124
|
+
routing: 'auto',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: 'offset-child-parent-link',
|
|
128
|
+
sourcePortId: 'offset-child-port',
|
|
129
|
+
targetPortId: 'offset-parent-port',
|
|
130
|
+
points: [],
|
|
131
|
+
routing: 'auto',
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
texts: [
|
|
135
|
+
{
|
|
136
|
+
id: 'offset-anchor-intro',
|
|
137
|
+
content:
|
|
138
|
+
'Offset attach anchors now keep endpoint hosts forbidden during auto routing. Inspect that routes exit from projected border sides instead of cutting through the source or target host interior.',
|
|
139
|
+
position: { x: 40, y: 28 },
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: 'offset-sibling-label',
|
|
143
|
+
content: 'Scenario A: sibling children use external attach points offset beyond left/right edges.',
|
|
144
|
+
position: { x: 52, y: 82 },
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: 'offset-direction-label',
|
|
148
|
+
content: 'Scenario B: parent/child links reuse the same offset anchors but resolve internal vs external semantics by direction.',
|
|
149
|
+
position: { x: 620, y: 82 },
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: 'offset-sibling-a-text',
|
|
153
|
+
content: 'Child A\nexternal offset 24',
|
|
154
|
+
ownerId: 'offset-sibling-a',
|
|
155
|
+
position: { x: 14, y: -20 },
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: 'offset-sibling-b-text',
|
|
159
|
+
content: 'Child B\nexternal offset 24',
|
|
160
|
+
ownerId: 'offset-sibling-b',
|
|
161
|
+
position: { x: 14, y: -20 },
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'offset-direction-parent-text',
|
|
165
|
+
content: 'Parent port\ninternal 18 / external 18',
|
|
166
|
+
ownerId: 'offset-direction-parent',
|
|
167
|
+
position: { x: 48, y: -20 },
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: 'offset-direction-child-text',
|
|
171
|
+
content: 'Child port\nexternal 18 / internal 18',
|
|
172
|
+
ownerId: 'offset-direction-child',
|
|
173
|
+
position: { x: 6, y: -18 },
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: 'offset-direction-links-text',
|
|
177
|
+
content: 'Blue links: parent→child and child→parent should keep the host border side semantics even with offset attach coordinates.',
|
|
178
|
+
position: { x: 620, y: 378 },
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const rerouteAllAction: DemoAction = {
|
|
184
|
+
id: 'offset-reroute-links',
|
|
185
|
+
label: 'Reroute All Links',
|
|
186
|
+
run: (editor) => {
|
|
187
|
+
editor.rerouteAllLinks();
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const shiftTargetsAction: DemoAction = {
|
|
192
|
+
id: 'offset-shift-targets',
|
|
193
|
+
label: 'Shift targets + reroute',
|
|
194
|
+
run: (editor) => {
|
|
195
|
+
editor.moveElementTo('offset-sibling-b', 320, 96);
|
|
196
|
+
editor.moveElementTo('offset-direction-child', 74, 96);
|
|
197
|
+
editor.rerouteAllLinks();
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export const offsetAnchorAvoidanceDemoConfig: DemoConfig = {
|
|
202
|
+
id: 'offset-anchor-avoidance',
|
|
203
|
+
title: 'Offset Anchor Avoidance',
|
|
204
|
+
description: 'Visual QA for projected-boundary-equivalent offset attach anchors that must still keep endpoint host interiors forbidden.',
|
|
205
|
+
createState: createOffsetAnchorAvoidanceState,
|
|
206
|
+
elementShapes: offsetAnchorShapes,
|
|
207
|
+
portShapes: basePortShapes,
|
|
208
|
+
defaultElementShapeId: 'default',
|
|
209
|
+
defaultPortShapeId: 'port-circle',
|
|
210
|
+
actions: [rerouteAllAction, shiftTargetsAction],
|
|
211
|
+
};
|
|
@@ -43,11 +43,11 @@ const createRoutingDemoState = (): DiagramState => ({
|
|
|
43
43
|
],
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
export const routingDemoConfig: DemoConfig = ({
|
|
47
|
-
id: 'routing-demo',
|
|
48
|
-
title: 'Routing Demo',
|
|
49
|
-
description: 'Auto routing
|
|
50
|
-
createState: createRoutingDemoState,
|
|
46
|
+
export const routingDemoConfig: DemoConfig = ({
|
|
47
|
+
id: 'routing-demo',
|
|
48
|
+
title: 'Routing Demo',
|
|
49
|
+
description: 'Auto routing is obstacle-aware. Toggle routing to compare manual vs auto reroute.',
|
|
50
|
+
createState: createRoutingDemoState,
|
|
51
51
|
elementShapes: baseElementShapes,
|
|
52
52
|
portShapes: basePortShapes,
|
|
53
53
|
defaultElementShapeId: 'default',
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { CSSProperties } from 'react';
|
|
2
|
-
import type { SimpleShape } from '../../api';
|
|
3
|
-
import type { ToolboxItem } from '../types';
|
|
4
|
-
import type { RouterStrategy } from '../../strategies/RouterStrategy';
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import type { SimpleShape } from '../../api';
|
|
3
|
+
import type { ToolboxItem } from '../types';
|
|
4
|
+
import type { RouterStrategy } from '../../strategies/RouterStrategy';
|
|
5
|
+
import ObstacleRouter from '../../strategies/ObstacleRouter';
|
|
5
6
|
|
|
6
7
|
export const baseElementShapes: SimpleShape[] = [
|
|
7
8
|
{
|
|
@@ -135,14 +136,18 @@ export const externalToolboxItems: ToolboxItem[] = [
|
|
|
135
136
|
|
|
136
137
|
export const defaultGhostSize = { width: 180, height: 120 };
|
|
137
138
|
|
|
138
|
-
export const defaultSvgPath = 'M10 10 H110 V90 H10 Z';
|
|
139
|
-
export const defaultSvgPathSize = { width: 180, height: 120 };
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
139
|
+
export const defaultSvgPath = 'M10 10 H110 V90 H10 Z';
|
|
140
|
+
export const defaultSvgPathSize = { width: 180, height: 120 };
|
|
141
|
+
const obstacleAwareRouter = new ObstacleRouter();
|
|
142
|
+
|
|
143
|
+
export const orthogonalRouter: RouterStrategy = {
|
|
144
|
+
route: (source, target, context) => {
|
|
145
|
+
if ((context?.obstacles?.length ?? 0) > 0) {
|
|
146
|
+
return obstacleAwareRouter.route(source, target, context);
|
|
147
|
+
}
|
|
148
|
+
if (Math.abs(source.x - target.x) < 1 || Math.abs(source.y - target.y) < 1) {
|
|
149
|
+
return [{ ...source }, { ...target }];
|
|
150
|
+
}
|
|
146
151
|
const midX = Math.round((source.x + target.x) / 2);
|
|
147
152
|
return [
|
|
148
153
|
{ ...source },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DiagramState } from '../../api';
|
|
1
|
+
import type { DiagramState, TextInteractionPolicy } from '../../api';
|
|
2
2
|
import type { DemoConfig } from '../types';
|
|
3
3
|
import { baseElementShapes, basePortShapes } from './shared';
|
|
4
4
|
|
|
@@ -7,6 +7,8 @@ export const textDemoOwnedTextId = 'text-owned-overflow';
|
|
|
7
7
|
export const textDemoFloatingTextId = 'text-floating-overflow';
|
|
8
8
|
export const textDemoLongSample =
|
|
9
9
|
'Long diagram label: middle ellipsis should keep the beginning and end visible while preserving the full canonical content in model state.';
|
|
10
|
+
export const textDemoOwnedInteraction: TextInteractionPolicy = { movable: false, editable: false };
|
|
11
|
+
export const textDemoFloatingInteraction: TextInteractionPolicy = { movable: true, editable: true };
|
|
10
12
|
|
|
11
13
|
const createTextOwnershipState = (): DiagramState => ({
|
|
12
14
|
elements: [
|
|
@@ -25,6 +27,7 @@ const createTextOwnershipState = (): DiagramState => ({
|
|
|
25
27
|
content: textDemoLongSample,
|
|
26
28
|
position: { x: 12, y: 12 },
|
|
27
29
|
ownerId: textDemoOwnerId,
|
|
30
|
+
interaction: textDemoOwnedInteraction,
|
|
28
31
|
layout: {
|
|
29
32
|
boundsMode: 'owner-width',
|
|
30
33
|
wrap: 'none',
|
|
@@ -36,6 +39,7 @@ const createTextOwnershipState = (): DiagramState => ({
|
|
|
36
39
|
id: textDemoFloatingTextId,
|
|
37
40
|
content: textDemoLongSample,
|
|
38
41
|
position: { x: 470, y: 120 },
|
|
42
|
+
interaction: textDemoFloatingInteraction,
|
|
39
43
|
layout: {
|
|
40
44
|
boundsMode: 'fixed',
|
|
41
45
|
fixedSize: { width: 240, height: 90 },
|
|
@@ -52,7 +56,7 @@ export const textDemoConfig: DemoConfig = ({
|
|
|
52
56
|
id: 'text',
|
|
53
57
|
title: 'Text Layout + Overflow',
|
|
54
58
|
description:
|
|
55
|
-
'Owner-constrained and fixed-bounds text with wrap and
|
|
59
|
+
'Owner-constrained and fixed-bounds text with wrap, ellipsis, and per-text interaction behavior. Full content is preserved in model state; truncation is visual-only.',
|
|
56
60
|
createState: createTextOwnershipState,
|
|
57
61
|
elementShapes: baseElementShapes,
|
|
58
62
|
portShapes: basePortShapes,
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { DiagramState } from '../../api';
|
|
2
|
+
import type { DemoAction, DemoConfig } from '../types';
|
|
3
|
+
import { baseElementShapes, basePortShapes } from './shared';
|
|
4
|
+
|
|
5
|
+
export const createZoomToFitElementsState = (): DiagramState => ({
|
|
6
|
+
elements: [
|
|
7
|
+
{
|
|
8
|
+
id: 'fit-node-a',
|
|
9
|
+
position: { x: 40, y: 60 },
|
|
10
|
+
size: { width: 140, height: 90 },
|
|
11
|
+
shapeId: 'default',
|
|
12
|
+
style: { fill: '#eef6ff', stroke: '#1d4ed8', strokeWidth: 2 },
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: 'fit-node-b',
|
|
16
|
+
position: { x: 760, y: 120 },
|
|
17
|
+
size: { width: 170, height: 110 },
|
|
18
|
+
shapeId: 'panel',
|
|
19
|
+
style: { fill: '#fef3c7', stroke: '#b45309', strokeWidth: 2 },
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'fit-node-c',
|
|
23
|
+
position: { x: 140, y: 420 },
|
|
24
|
+
size: { width: 220, height: 120 },
|
|
25
|
+
shapeId: 'default',
|
|
26
|
+
style: { fill: '#ecfdf5', stroke: '#047857', strokeWidth: 2 },
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'fit-node-d',
|
|
30
|
+
position: { x: 860, y: 430 },
|
|
31
|
+
size: { width: 150, height: 90 },
|
|
32
|
+
shapeId: 'default',
|
|
33
|
+
style: { fill: '#f5f3ff', stroke: '#6d28d9', strokeWidth: 2 },
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
ports: [],
|
|
37
|
+
links: [],
|
|
38
|
+
texts: [
|
|
39
|
+
{
|
|
40
|
+
id: 'fit-text-intro',
|
|
41
|
+
content: 'Use actions to spread nodes and run the zoom-to-fit viewport command.',
|
|
42
|
+
position: { x: 40, y: 24 },
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const zoomFitAction: DemoAction = {
|
|
48
|
+
id: 'fit-zoom-default',
|
|
49
|
+
label: 'Run Zoom-To-Fit',
|
|
50
|
+
run: (editor) => {
|
|
51
|
+
editor.zoomToFitElements();
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const zoomFitPaddingAction: DemoAction = {
|
|
56
|
+
id: 'fit-zoom-padding',
|
|
57
|
+
label: 'Run Zoom-To-Fit (Padding 48)',
|
|
58
|
+
run: (editor) => {
|
|
59
|
+
editor.zoomToFitElements({ padding: 48 });
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const spreadSceneAction: DemoAction = {
|
|
64
|
+
id: 'fit-spread-scene',
|
|
65
|
+
label: 'Spread Scene',
|
|
66
|
+
run: (editor) => {
|
|
67
|
+
editor.moveElementTo('fit-node-b', 880, 80);
|
|
68
|
+
editor.moveElementTo('fit-node-d', 980, 500);
|
|
69
|
+
editor.moveElementTo('fit-node-c', 80, 520);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const zoomToFitElementsDemoConfig: DemoConfig = {
|
|
74
|
+
id: 'zoom-to-fit-elements',
|
|
75
|
+
title: 'Zoom To Fit Elements',
|
|
76
|
+
description: 'Calls a viewport command that zooms and centers so all elements are visible.',
|
|
77
|
+
createState: createZoomToFitElementsState,
|
|
78
|
+
elementShapes: baseElementShapes,
|
|
79
|
+
portShapes: basePortShapes,
|
|
80
|
+
defaultElementShapeId: 'default',
|
|
81
|
+
defaultPortShapeId: 'port-circle',
|
|
82
|
+
actions: [zoomFitAction, zoomFitPaddingAction, spreadSceneAction],
|
|
83
|
+
};
|
package/src/displaybox/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { CSSProperties } from 'react';
|
|
2
|
-
import type { DiagramEditorHandle, DiagramState, SimpleShape } from '../api';
|
|
3
|
-
import type { RouterStrategy } from '../strategies/RouterStrategy';
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import type { DiagramEditorHandle, DiagramState, LinkColorPoolPolicy, LinkRouteRefreshPolicy, SimpleShape } from '../api';
|
|
3
|
+
import type { RouterStrategy } from '../strategies/RouterStrategy';
|
|
4
4
|
|
|
5
5
|
export type DemoActionHelpers = {
|
|
6
6
|
nextOffset: () => number;
|
|
@@ -26,11 +26,13 @@ export type DemoConfig = {
|
|
|
26
26
|
portShapes?: SimpleShape[];
|
|
27
27
|
defaultElementShapeId?: string;
|
|
28
28
|
defaultPortShapeId?: string;
|
|
29
|
-
router?: RouterStrategy;
|
|
30
|
-
snapDefault?: boolean;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
router?: RouterStrategy;
|
|
30
|
+
snapDefault?: boolean;
|
|
31
|
+
linkRouteRefreshPolicy?: LinkRouteRefreshPolicy;
|
|
32
|
+
linkColorPoolPolicy?: LinkColorPoolPolicy;
|
|
33
|
+
stageStyle?: CSSProperties;
|
|
34
|
+
actions: DemoAction[];
|
|
35
|
+
};
|
|
34
36
|
|
|
35
37
|
export type DemoDefinition = {
|
|
36
38
|
id: string;
|