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.
Files changed (75) hide show
  1. package/CHANGELOG.md +42 -4
  2. package/README.md +15 -32
  3. package/ai/api-contract.json +88 -3
  4. package/ai/manifest.json +1 -1
  5. package/dist/cjs/examples.js +6519 -4423
  6. package/dist/cjs/index.js +1513 -120
  7. package/dist/cjs/types/api/createDiagramEditor.d.ts +4 -1
  8. package/dist/cjs/types/api/types.d.ts +43 -1
  9. package/dist/cjs/types/displaybox/demos/LinkColorPoolDemoTab.d.ts +3 -0
  10. package/dist/cjs/types/displaybox/demos/OffsetAnchorAvoidanceDemoTab.d.ts +3 -0
  11. package/dist/cjs/types/displaybox/demos/SimpleDemo.d.ts +4 -1
  12. package/dist/cjs/types/displaybox/demos/ZoomToFitElementsDemoTab.d.ts +3 -0
  13. package/dist/cjs/types/displaybox/demos/linkColorPoolDemo.d.ts +2 -0
  14. package/dist/cjs/types/displaybox/demos/offsetAnchorAvoidanceDemo.d.ts +4 -0
  15. package/dist/cjs/types/displaybox/demos/textDemo.d.ts +3 -0
  16. package/dist/cjs/types/displaybox/demos/zoomToFitElementsDemo.d.ts +4 -0
  17. package/dist/cjs/types/displaybox/types.d.ts +3 -1
  18. package/dist/cjs/types/displaybox/useDemoEditor.d.ts +4 -2
  19. package/dist/cjs/types/engine/AutoLayoutService.d.ts +59 -2
  20. package/dist/cjs/types/engine/DiagramEngine.d.ts +20 -1
  21. package/dist/cjs/types/engine/LinkRoutingService.d.ts +9 -0
  22. package/dist/cjs/types/models/TextModel.d.ts +1 -0
  23. package/dist/cjs/types/renderer/konva/KonvaInteraction.d.ts +6 -0
  24. package/dist/cjs/types/strategies/ObstacleRouter.d.ts +2 -0
  25. package/dist/cjs/types/strategies/RouterStrategy.d.ts +10 -0
  26. package/dist/esm/examples.js +6519 -4423
  27. package/dist/esm/examples.js.map +1 -1
  28. package/dist/esm/index.js +1513 -120
  29. package/dist/esm/index.js.map +1 -1
  30. package/dist/esm/types/api/createDiagramEditor.d.ts +4 -1
  31. package/dist/esm/types/api/types.d.ts +43 -1
  32. package/dist/esm/types/displaybox/demos/LinkColorPoolDemoTab.d.ts +3 -0
  33. package/dist/esm/types/displaybox/demos/OffsetAnchorAvoidanceDemoTab.d.ts +3 -0
  34. package/dist/esm/types/displaybox/demos/SimpleDemo.d.ts +4 -1
  35. package/dist/esm/types/displaybox/demos/ZoomToFitElementsDemoTab.d.ts +3 -0
  36. package/dist/esm/types/displaybox/demos/linkColorPoolDemo.d.ts +2 -0
  37. package/dist/esm/types/displaybox/demos/offsetAnchorAvoidanceDemo.d.ts +4 -0
  38. package/dist/esm/types/displaybox/demos/textDemo.d.ts +3 -0
  39. package/dist/esm/types/displaybox/demos/zoomToFitElementsDemo.d.ts +4 -0
  40. package/dist/esm/types/displaybox/types.d.ts +3 -1
  41. package/dist/esm/types/displaybox/useDemoEditor.d.ts +4 -2
  42. package/dist/esm/types/engine/AutoLayoutService.d.ts +59 -2
  43. package/dist/esm/types/engine/DiagramEngine.d.ts +20 -1
  44. package/dist/esm/types/engine/LinkRoutingService.d.ts +9 -0
  45. package/dist/esm/types/models/TextModel.d.ts +1 -0
  46. package/dist/esm/types/renderer/konva/KonvaInteraction.d.ts +6 -0
  47. package/dist/esm/types/strategies/ObstacleRouter.d.ts +2 -0
  48. package/dist/esm/types/strategies/RouterStrategy.d.ts +10 -0
  49. package/dist/examples.d.ts +56 -1
  50. package/dist/index.d.ts +60 -2
  51. package/docs/API_CONTRACT.md +73 -3
  52. package/docs/CAPABILITIES.md +8 -0
  53. package/docs/COMMANDS_EVENTS.md +8 -0
  54. package/docs/DOCUMENTATION_WORKFLOW.md +4 -1
  55. package/docs/INTEGRATION_PLAYBOOK.md +3 -0
  56. package/docs/STATE_INVARIANTS.md +4 -0
  57. package/package.json +1 -1
  58. package/src/displaybox/demos/AutoLayoutDemoTab.tsx +515 -381
  59. package/src/displaybox/demos/EngineEventsDemoTab.tsx +12 -11
  60. package/src/displaybox/demos/LinkColorPoolDemoTab.tsx +49 -0
  61. package/src/displaybox/demos/ObstacleRoutingDemoTab.tsx +52 -31
  62. package/src/displaybox/demos/OffsetAnchorAvoidanceDemoTab.tsx +30 -0
  63. package/src/displaybox/demos/SimpleDemo.tsx +18 -13
  64. package/src/displaybox/demos/TextLayoutDemoTab.tsx +18 -1
  65. package/src/displaybox/demos/ZoomToFitElementsDemoTab.tsx +29 -0
  66. package/src/displaybox/demos/autoLayoutDemo.ts +62 -26
  67. package/src/displaybox/demos/index.tsx +81 -57
  68. package/src/displaybox/demos/linkColorPoolDemo.ts +122 -0
  69. package/src/displaybox/demos/obstacleRoutingDemo.ts +77 -1
  70. package/src/displaybox/demos/offsetAnchorAvoidanceDemo.ts +211 -0
  71. package/src/displaybox/demos/routingDemo.ts +5 -5
  72. package/src/displaybox/demos/shared.ts +17 -12
  73. package/src/displaybox/demos/textDemo.ts +6 -2
  74. package/src/displaybox/demos/zoomToFitElementsDemo.ts +83 -0
  75. package/src/displaybox/types.ts +10 -8
@@ -22,17 +22,18 @@ const engineEventOptions = [
22
22
  'elementClick',
23
23
  'elementDragged',
24
24
  'elementMoved',
25
- 'elementResized',
26
- 'elementDeleted',
27
- 'portDeleted',
28
- 'linkDeleted',
29
- 'textDeleted',
30
- 'elementSelected',
31
- 'elementLinkStarted',
32
- 'elementLinkConnecting',
33
- 'elementLinkEnded',
34
- 'textSelected',
35
- ] as const;
25
+ 'elementResized',
26
+ 'gridLayoutChanged',
27
+ 'elementDeleted',
28
+ 'portDeleted',
29
+ 'linkDeleted',
30
+ 'textDeleted',
31
+ 'elementSelected',
32
+ 'elementLinkStarted',
33
+ 'elementLinkConnecting',
34
+ 'elementLinkEnded',
35
+ 'textSelected',
36
+ ] as const;
36
37
  type EngineEventKey = typeof engineEventOptions[number];
37
38
 
38
39
  const formatEventPayload = (payload: EngineEventMap[EngineEventKey] | null) =>
@@ -0,0 +1,49 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import SimpleDemo from './SimpleDemo';
3
+ import { linkColorPoolDemoConfig } from './linkColorPoolDemo';
4
+
5
+ type LinkColorMode = 'off' | 'default' | 'custom';
6
+
7
+ const customPool = ['#0b7285', '#e8590c', '#5f3dc4'];
8
+
9
+ const LinkColorPoolDemo = () => {
10
+ const [mode, setMode] = useState<LinkColorMode>('off');
11
+ const linkColorPoolPolicy = useMemo(() => {
12
+ if (mode === 'off') return undefined;
13
+ if (mode === 'default') return { enabled: true };
14
+ return { enabled: true, colors: customPool };
15
+ }, [mode]);
16
+
17
+ return (
18
+ <SimpleDemo
19
+ demo={linkColorPoolDemoConfig}
20
+ linkColorPoolPolicy={linkColorPoolPolicy}
21
+ beforeStage={
22
+ <div
23
+ style={{
24
+ marginBottom: 12,
25
+ padding: '10px 12px',
26
+ borderRadius: 8,
27
+ border: '1px solid #2d2d2d',
28
+ background: '#f4f4f4',
29
+ }}
30
+ >
31
+ <label htmlFor="link-color-mode" style={{ display: 'inline-flex', alignItems: 'center', gap: 8, fontWeight: 600 }}>
32
+ Color Mode
33
+ <select
34
+ id="link-color-mode"
35
+ value={mode}
36
+ onChange={(event) => setMode(event.target.value as LinkColorMode)}
37
+ >
38
+ <option value="off">Off (default behavior)</option>
39
+ <option value="default">Default 20-color pool</option>
40
+ <option value="custom">Custom pool (#0b7285, #e8590c, #5f3dc4)</option>
41
+ </select>
42
+ </label>
43
+ </div>
44
+ }
45
+ />
46
+ );
47
+ };
48
+
49
+ export default LinkColorPoolDemo;
@@ -1,31 +1,52 @@
1
- import React from 'react';
2
- import SimpleDemo from './SimpleDemo';
3
- import { obstacleRoutingDemoConfig } from './obstacleRoutingDemo';
4
-
5
- const ObstacleRoutingDemo = () => (
6
- <SimpleDemo
7
- demo={obstacleRoutingDemoConfig}
8
- beforeStage={
9
- <div
10
- style={{
11
- marginBottom: 12,
12
- padding: '10px 12px',
13
- borderRadius: 8,
14
- border: '1px solid #2d2d2d',
15
- background: '#f4f4f4',
16
- }}
17
- >
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
- />
29
- );
30
-
31
- export default ObstacleRoutingDemo;
1
+ import React, { useMemo, useState } from 'react';
2
+ import SimpleDemo from './SimpleDemo';
3
+ import { obstacleRoutingDemoConfig } from './obstacleRoutingDemo';
4
+
5
+ const ObstacleRoutingDemo = () => {
6
+ const [redrawRefreshEnabled, setRedrawRefreshEnabled] = useState(false);
7
+ const linkRouteRefreshPolicy = useMemo(
8
+ () =>
9
+ redrawRefreshEnabled
10
+ ? { mode: 'redraw-two-endpoint-change' as const, includeManual: false }
11
+ : undefined,
12
+ [redrawRefreshEnabled],
13
+ );
14
+
15
+ return (
16
+ <SimpleDemo
17
+ demo={obstacleRoutingDemoConfig}
18
+ linkRouteRefreshPolicy={linkRouteRefreshPolicy}
19
+ beforeStage={
20
+ <div
21
+ style={{
22
+ marginBottom: 12,
23
+ padding: '10px 12px',
24
+ borderRadius: 8,
25
+ border: '1px solid #2d2d2d',
26
+ background: '#f4f4f4',
27
+ }}
28
+ >
29
+ <strong>Expected routing behavior</strong>
30
+ <ul style={{ margin: '6px 0 10px 16px' }}>
31
+ <li>Scenario A: the sibling link must not cross either child host interior.</li>
32
+ <li>Scenario B: parent endpoints use internal attach points while child/sibling endpoints use external attach points.</li>
33
+ <li>Scenario C: cross-parent link should exit source branch early, travel along the first common-ancestor corridor, then descend near target.</li>
34
+ <li>Use `Reroute All Links` repeatedly to verify deterministic routing output.</li>
35
+ <li>For redraw refresh check: keep the stale cross-branch link, toggle the option below, then click `Manual Render`.</li>
36
+ </ul>
37
+ <label htmlFor="obstacle-redraw-refresh-toggle" style={{ display: 'inline-flex', alignItems: 'center', gap: 8, fontWeight: 600 }}>
38
+ <input
39
+ id="obstacle-redraw-refresh-toggle"
40
+ type="checkbox"
41
+ checked={redrawRefreshEnabled}
42
+ onChange={(event) => setRedrawRefreshEnabled(event.target.checked)}
43
+ />
44
+ Enable redraw refresh on two-endpoint change
45
+ </label>
46
+ </div>
47
+ }
48
+ />
49
+ );
50
+ };
51
+
52
+ export default ObstacleRoutingDemo;
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import SimpleDemo from './SimpleDemo';
3
+ import { offsetAnchorAvoidanceDemoConfig } from './offsetAnchorAvoidanceDemo';
4
+
5
+ const OffsetAnchorAvoidanceDemo = () => (
6
+ <SimpleDemo
7
+ demo={offsetAnchorAvoidanceDemoConfig}
8
+ beforeStage={
9
+ <div
10
+ style={{
11
+ marginBottom: 12,
12
+ padding: '10px 12px',
13
+ borderRadius: 8,
14
+ border: '1px solid #2d2d2d',
15
+ background: '#f4f4f4',
16
+ }}
17
+ >
18
+ <strong>Visual checks</strong>
19
+ <ul style={{ margin: '6px 0 0 16px' }}>
20
+ <li>Sibling link should route around both child interiors even though attach coordinates are offset outside the host edges.</li>
21
+ <li>`parent→child` should use the parent&apos;s internal side and the child&apos;s external side.</li>
22
+ <li>`child→parent` should invert the same endpoint semantics without crossing either endpoint host interior.</li>
23
+ <li>Use `Shift targets + reroute` and `Reroute All Links` to verify stable behavior after layout changes.</li>
24
+ </ul>
25
+ </div>
26
+ }
27
+ />
28
+ );
29
+
30
+ export default OffsetAnchorAvoidanceDemo;
@@ -1,14 +1,17 @@
1
- import React from 'react';
2
- import DisplayBoxControls from '../DisplayBoxControls';
1
+ import React from 'react';
2
+ import type { LinkColorPoolPolicy, LinkRouteRefreshPolicy } from '../../api';
3
+ import DisplayBoxControls from '../DisplayBoxControls';
3
4
  import DisplayBoxStage from '../DisplayBoxStage';
4
5
  import useDemoControls from '../useDemoControls';
5
6
  import useDemoEditor from '../useDemoEditor';
6
7
  import useOffsetSequence from '../useOffsetSequence';
7
8
  import type { DemoConfig, DemoActionHelpers } from '../types';
8
9
 
9
- type SimpleDemoProps = {
10
- demo: DemoConfig;
11
- beforeStage?: React.ReactNode;
10
+ type SimpleDemoProps = {
11
+ demo: DemoConfig;
12
+ linkRouteRefreshPolicy?: LinkRouteRefreshPolicy;
13
+ linkColorPoolPolicy?: LinkColorPoolPolicy;
14
+ beforeStage?: React.ReactNode;
12
15
  stageHandlers?: {
13
16
  onDragOver?: (event: React.DragEvent<HTMLDivElement>) => void;
14
17
  onDragLeave?: () => void;
@@ -16,14 +19,16 @@ type SimpleDemoProps = {
16
19
  };
17
20
  };
18
21
 
19
- const SimpleDemo = ({ demo, beforeStage, stageHandlers }: SimpleDemoProps) => {
20
- const { containerRef, editorRef, diagramState, selection, snapEnabled, setSnapEnabled } = useDemoEditor({
21
- createState: demo.createState,
22
- elementShapes: demo.elementShapes,
23
- portShapes: demo.portShapes,
24
- router: demo.router,
25
- snapDefault: demo.snapDefault,
26
- });
22
+ const SimpleDemo = ({ demo, linkRouteRefreshPolicy, linkColorPoolPolicy, beforeStage, stageHandlers }: SimpleDemoProps) => {
23
+ const { containerRef, editorRef, diagramState, selection, snapEnabled, setSnapEnabled } = useDemoEditor({
24
+ createState: demo.createState,
25
+ elementShapes: demo.elementShapes,
26
+ portShapes: demo.portShapes,
27
+ router: demo.router,
28
+ snapDefault: demo.snapDefault,
29
+ linkRouteRefreshPolicy: linkRouteRefreshPolicy ?? demo.linkRouteRefreshPolicy,
30
+ linkColorPoolPolicy: linkColorPoolPolicy ?? demo.linkColorPoolPolicy,
31
+ });
27
32
 
28
33
  const nextOffset = useOffsetSequence();
29
34
  const actionHelpers: DemoActionHelpers = React.useMemo(() => ({ nextOffset }), [nextOffset]);
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useMemo, useState } from 'react';
2
- import type { TextLayout } from '../../api';
2
+ import type { TextInteractionPolicy, TextLayout } from '../../api';
3
3
  import DisplayBoxControls from '../DisplayBoxControls';
4
4
  import DisplayBoxStage from '../DisplayBoxStage';
5
5
  import useDemoControls from '../useDemoControls';
@@ -9,8 +9,10 @@ import type { DemoActionHelpers } from '../types';
9
9
  import {
10
10
  textDemoConfig,
11
11
  textDemoFloatingTextId,
12
+ textDemoFloatingInteraction,
12
13
  textDemoLongSample,
13
14
  textDemoOwnedTextId,
15
+ textDemoOwnedInteraction,
14
16
  textDemoOwnerId,
15
17
  } from './textDemo';
16
18
 
@@ -58,10 +60,15 @@ const TextLayoutDemo = () => {
58
60
  const selectedText = diagramState?.texts.find((text) => text.id === selectedTextId);
59
61
  const selectedDisplayContent = selectedText?.displayContent ?? selectedText?.content ?? '';
60
62
  const ownerElement = diagramState?.elements.find((element) => element.id === textDemoOwnerId);
63
+ const selectedInteraction = selectedText?.interaction;
64
+
65
+ const getInteractionForTarget = (nextTarget: TextTarget): TextInteractionPolicy =>
66
+ nextTarget === 'owned' ? textDemoOwnedInteraction : textDemoFloatingInteraction;
61
67
 
62
68
  const applyLayoutToTarget = (nextTarget: TextTarget, nextLayout: TextLayout) => {
63
69
  const editor = editorRef.current;
64
70
  if (!editor) return;
71
+ const interaction = getInteractionForTarget(nextTarget);
65
72
  if (nextTarget === 'owned') {
66
73
  editor.removeText(textDemoOwnedTextId);
67
74
  editor.addText({
@@ -69,6 +76,7 @@ const TextLayoutDemo = () => {
69
76
  content: textDemoLongSample,
70
77
  ownerId: textDemoOwnerId,
71
78
  position: { x: 12, y: 12 },
79
+ interaction,
72
80
  layout: nextLayout,
73
81
  });
74
82
  editor.setSelection([textDemoOwnedTextId]);
@@ -80,6 +88,7 @@ const TextLayoutDemo = () => {
80
88
  id: textDemoFloatingTextId,
81
89
  content: textDemoLongSample,
82
90
  position: { x: 470, y: 120 },
91
+ interaction,
83
92
  layout: nextLayout,
84
93
  });
85
94
  editor.setSelection([textDemoFloatingTextId]);
@@ -163,6 +172,10 @@ const TextLayoutDemo = () => {
163
172
  Full text content is preserved in state. Overflow and ellipsis settings only change visual output
164
173
  (`displayContent`) for rendering.
165
174
  </p>
175
+ <p style={{ marginTop: 0, marginBottom: 0, fontSize: 13 }}>
176
+ Owned text is fixed and read-only. Floating text remains draggable and editable. Try drag and
177
+ double-click on each label to compare built-in interaction policy behavior.
178
+ </p>
166
179
  </div>
167
180
  <DisplayBoxControls
168
181
  actions={demo.actions}
@@ -370,6 +383,10 @@ const TextLayoutDemo = () => {
370
383
  ? `${selectedWorldX.toFixed(1)}, ${selectedWorldY.toFixed(1)}`
371
384
  : 'n/a'}
372
385
  </div>
386
+ <div>
387
+ interaction: movable={selectedInteraction?.movable === false ? 'false' : 'true'} | editable=
388
+ {selectedInteraction?.editable === false ? 'false' : 'true'}
389
+ </div>
373
390
  </div>
374
391
 
375
392
  <div style={{ display: 'grid', gap: 6 }}>
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import SimpleDemo from './SimpleDemo';
3
+ import { zoomToFitElementsDemoConfig } from './zoomToFitElementsDemo';
4
+
5
+ const ZoomToFitElementsDemo = () => (
6
+ <SimpleDemo
7
+ demo={zoomToFitElementsDemoConfig}
8
+ beforeStage={(
9
+ <div
10
+ style={{
11
+ marginBottom: 12,
12
+ padding: '10px 12px',
13
+ borderRadius: 8,
14
+ border: '1px solid #2d2d2d',
15
+ background: '#f4f4f4',
16
+ }}
17
+ >
18
+ <strong>Visual checks</strong>
19
+ <ul style={{ margin: '6px 0 0 16px' }}>
20
+ <li>Click `Run Zoom-To-Fit` and ensure every node is visible and centered.</li>
21
+ <li>Run `Spread Scene` then fit again to verify viewport recomputes from the updated layout.</li>
22
+ <li>Use `Run Zoom-To-Fit (Padding 48)` to confirm optional padding keeps extra breathing room around content.</li>
23
+ </ul>
24
+ </div>
25
+ )}
26
+ />
27
+ );
28
+
29
+ export default ZoomToFitElementsDemo;
@@ -36,6 +36,29 @@ const createAutoLayoutState = (): DiagramState => ({
36
36
  { id: 'col-b', position: { x: 0, y: 0 }, size: { width: 70, height: 36 }, shapeId: 'default', parentId: 'layout-column' },
37
37
  { id: 'col-c', position: { x: 0, y: 0 }, size: { width: 92, height: 44 }, shapeId: 'default', parentId: 'layout-column' },
38
38
 
39
+ {
40
+ id: 'layout-grid',
41
+ position: { x: 620, y: 70 },
42
+ size: { width: 160, height: 110 },
43
+ shapeId: 'panel',
44
+ layout: {
45
+ mode: 'grid',
46
+ padding: { x: 10, y: 10 },
47
+ gap: 10,
48
+ align: 'center',
49
+ autoResize: 'grow-shrink',
50
+ gridTemplate: [12, 4, 8],
51
+ gridChildWidthResizeEnabled: false,
52
+ childMinWidth: 24,
53
+ childMinHeight: 18,
54
+ },
55
+ },
56
+ { id: 'grid-a', position: { x: 0, y: 0 }, size: { width: 36, height: 26 }, shapeId: 'default', parentId: 'layout-grid' },
57
+ { id: 'grid-b', position: { x: 0, y: 0 }, size: { width: 44, height: 24 }, shapeId: 'default', parentId: 'layout-grid' },
58
+ { id: 'grid-c', position: { x: 0, y: 0 }, size: { width: 50, height: 20 }, shapeId: 'default', parentId: 'layout-grid' },
59
+ { id: 'grid-d', position: { x: 0, y: 0 }, size: { width: 28, height: 30 }, shapeId: 'default', parentId: 'layout-grid' },
60
+ { id: 'grid-e', position: { x: 0, y: 0 }, size: { width: 52, height: 28 }, shapeId: 'default', parentId: 'layout-grid' },
61
+
39
62
  {
40
63
  id: 'layout-nested',
41
64
  position: { x: 120, y: 260 },
@@ -74,36 +97,49 @@ const createAutoLayoutState = (): DiagramState => ({
74
97
  ports: [],
75
98
  links: [],
76
99
  texts: [
77
- {
78
- id: 'label-layout-row',
79
- content: 'Row parent label lane',
80
- position: { x: 8, y: 6 },
81
- ownerId: 'layout-row',
82
- layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
83
- },
100
+ {
101
+ id: 'label-layout-row',
102
+ content: 'Row parent label lane',
103
+ position: { x: 8, y: 6 },
104
+ ownerId: 'layout-row',
105
+ layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
106
+ },
84
107
  { id: 'label-row-a', content: 'row-a', position: { x: 6, y: -14 }, ownerId: 'row-a' },
85
108
  { id: 'label-row-b', content: 'row-b', position: { x: 6, y: -14 }, ownerId: 'row-b' },
86
109
  { id: 'label-row-c', content: 'row-c', position: { x: 6, y: -14 }, ownerId: 'row-c' },
87
110
  { id: 'label-row-nested-owner', content: 'owns children', position: { x: 6, y: -16 }, ownerId: 'row-nested-owner' },
88
111
 
89
- {
90
- id: 'label-layout-column',
91
- content: 'Column parent label lane',
92
- position: { x: 8, y: 6 },
93
- ownerId: 'layout-column',
94
- layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
95
- },
112
+ {
113
+ id: 'label-layout-column',
114
+ content: 'Column parent label lane',
115
+ position: { x: 8, y: 6 },
116
+ ownerId: 'layout-column',
117
+ layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
118
+ },
96
119
  { id: 'label-col-a', content: 'col-a', position: { x: 6, y: -14 }, ownerId: 'col-a' },
97
120
  { id: 'label-col-b', content: 'col-b', position: { x: 6, y: -14 }, ownerId: 'col-b' },
98
121
  { id: 'label-col-c', content: 'col-c', position: { x: 6, y: -14 }, ownerId: 'col-c' },
99
122
 
100
- {
101
- id: 'label-layout-nested',
102
- content: 'Row + nested column lane',
103
- position: { x: 6, y: 6 },
104
- ownerId: 'layout-nested',
105
- layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
106
- },
123
+ {
124
+ id: 'label-layout-grid',
125
+ content: 'Grid template [12,4,8]',
126
+ position: { x: 8, y: 6 },
127
+ ownerId: 'layout-grid',
128
+ layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
129
+ },
130
+ { id: 'label-grid-a', content: 'grid-a', position: { x: 6, y: -14 }, ownerId: 'grid-a' },
131
+ { id: 'label-grid-b', content: 'grid-b', position: { x: 6, y: -14 }, ownerId: 'grid-b' },
132
+ { id: 'label-grid-c', content: 'grid-c', position: { x: 6, y: -14 }, ownerId: 'grid-c' },
133
+ { id: 'label-grid-d', content: 'grid-d', position: { x: 6, y: -14 }, ownerId: 'grid-d' },
134
+ { id: 'label-grid-e', content: 'grid-e', position: { x: 6, y: -14 }, ownerId: 'grid-e' },
135
+
136
+ {
137
+ id: 'label-layout-nested',
138
+ content: 'Row + nested column lane',
139
+ position: { x: 6, y: 6 },
140
+ ownerId: 'layout-nested',
141
+ layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
142
+ },
107
143
  { id: 'label-nested-stack', content: 'nested stack', position: { x: 6, y: -16 }, ownerId: 'nested-stack' },
108
144
  { id: 'label-stack-a', content: 'stack-a', position: { x: 6, y: -14 }, ownerId: 'stack-a' },
109
145
  { id: 'label-stack-b', content: 'stack-b', position: { x: 6, y: -14 }, ownerId: 'stack-b' },
@@ -116,11 +152,11 @@ const createAutoLayoutState = (): DiagramState => ({
116
152
  ],
117
153
  });
118
154
 
119
- export const autoLayoutDemoConfig: DemoConfig = ({
120
- id: 'auto-layout',
121
- title: 'Element Auto-Layout + Label Lane',
122
- description: 'Consolidated auto-layout verification with baseline/nested containers, fit options, and label reserved-space modes.',
123
- createState: createAutoLayoutState,
155
+ export const autoLayoutDemoConfig: DemoConfig = ({
156
+ id: 'auto-layout',
157
+ title: 'Element Auto-Layout + Label Lane',
158
+ description: 'Consolidated auto-layout verification with horizontal/vertical/grid containers, fit options, auto-resize policies, and label reserved-space modes.',
159
+ createState: createAutoLayoutState,
124
160
  elementShapes: baseElementShapes,
125
161
  portShapes: basePortShapes,
126
162
  defaultElementShapeId: 'default',
@@ -8,8 +8,8 @@ import { textDemoConfig } from './textDemo';
8
8
  import { selectionDemoConfig } from './selectionDemo';
9
9
  import { eventHandlersDemoConfig } from './eventHandlersDemo';
10
10
  import { engineEventsDemoConfig } from './engineEventsDemo';
11
- import { linkCancelDemoConfig } from './linkCancelDemo';
12
- import { autoLayoutDemoConfig } from './autoLayoutDemo';
11
+ import { linkCancelDemoConfig } from './linkCancelDemo';
12
+ import { autoLayoutDemoConfig } from './autoLayoutDemo';
13
13
  import { externalDragDropDemoConfig } from './externalDragDropDemo';
14
14
  import { shapeGalleryDemoConfig } from './shapeGalleryDemo';
15
15
  import { svgPathDemoConfig } from './svgPathDemo';
@@ -18,32 +18,38 @@ 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 { portPositionLimitsDemoConfig } from './portPositionLimitsDemo';
23
- import { portBorderDemoConfig } from './portBorderDemo';
24
- import { childConstraintsDemoConfig } from './childConstraintsDemo';
25
- import { labelStyleDemoConfig } from './labelStyleDemo';
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';
26
+ import { linkColorPoolDemoConfig } from './linkColorPoolDemo';
26
27
  import { gridOverlayDemoConfig } from './gridOverlayDemo';
27
28
  import { routingDemoConfig } from './routingDemo';
28
29
  import { obstacleRoutingDemoConfig } from './obstacleRoutingDemo';
29
30
  import { linkBendHandlesDemoConfig } from './linkBendHandlesDemo';
30
31
  import { deletionEventsDemoConfig } from './deletionEventsDemo';
31
- import { shapeBorderMovementDemoConfig } from './shapeBorderMovementDemo';
32
- import { shapeHoverControlsDemoConfig } from './shapeHoverControlsDemo';
33
- import { vertexControlLinkSessionDemoConfig } from './vertexControlLinkSessionDemo';
32
+ import { shapeBorderMovementDemoConfig } from './shapeBorderMovementDemo';
33
+ import { shapeHoverControlsDemoConfig } from './shapeHoverControlsDemo';
34
+ import { vertexControlLinkSessionDemoConfig } from './vertexControlLinkSessionDemo';
34
35
  import EventHandlersDemo from './EventHandlersDemoTab';
35
36
  import EngineEventsDemo from './EngineEventsDemoTab';
36
- import LinkCancelDemo from './LinkCancelDemoTab';
37
- import AutoLayoutDemo from './AutoLayoutDemoTab';
37
+ import LinkCancelDemo from './LinkCancelDemoTab';
38
+ import AutoLayoutDemo from './AutoLayoutDemoTab';
38
39
  import ExternalDragDropDemo from './ExternalDragDropDemoTab';
39
40
  import SvgPathDemo from './SvgPathDemoTab';
40
41
  import TextLayoutDemo from './TextLayoutDemoTab';
41
42
  import SimpleDemo from './SimpleDemo';
42
- import ObstacleRoutingDemo from './ObstacleRoutingDemoTab';
43
- import DeletionEventsDemo from './DeletionEventsDemoTab';
43
+ import ObstacleRoutingDemo from './ObstacleRoutingDemoTab';
44
+ import OffsetAnchorAvoidanceDemo from './OffsetAnchorAvoidanceDemoTab';
45
+ import ZoomToFitElementsDemo from './ZoomToFitElementsDemoTab';
46
+ import DeletionEventsDemo from './DeletionEventsDemoTab';
44
47
  import ShapeHoverControlsDemo from './ShapeHoverControlsDemoTab';
48
+ import LinkColorPoolDemo from './LinkColorPoolDemoTab';
45
49
  import AsymmetricPortMultiAnchorDemo from './AsymmetricPortMultiAnchorDemoTab';
46
50
  import VertexControlLinkSessionDemo from './VertexControlLinkSessionDemoTab';
51
+ import { offsetAnchorAvoidanceDemoConfig } from './offsetAnchorAvoidanceDemo';
52
+ import { zoomToFitElementsDemoConfig } from './zoomToFitElementsDemo';
47
53
 
48
54
  export {
49
55
  baseElementShapes,
@@ -112,12 +118,12 @@ export const demoTabs: DemoDefinition[] = [
112
118
  description: deletionEventsDemoConfig.description,
113
119
  Component: DeletionEventsDemo,
114
120
  },
115
- {
116
- id: autoLayoutDemoConfig.id,
117
- title: autoLayoutDemoConfig.title,
118
- description: autoLayoutDemoConfig.description,
119
- Component: AutoLayoutDemo,
120
- },
121
+ {
122
+ id: autoLayoutDemoConfig.id,
123
+ title: autoLayoutDemoConfig.title,
124
+ description: autoLayoutDemoConfig.description,
125
+ Component: AutoLayoutDemo,
126
+ },
121
127
  {
122
128
  id: linkCancelDemoConfig.id,
123
129
  title: linkCancelDemoConfig.title,
@@ -172,28 +178,34 @@ export const demoTabs: DemoDefinition[] = [
172
178
  description: multipleElementsDemoConfig.description,
173
179
  Component: wrapSimpleDemo(multipleElementsDemoConfig),
174
180
  },
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,
181
+ {
182
+ id: portConstraintsDemoConfig.id,
183
+ title: portConstraintsDemoConfig.title,
184
+ description: portConstraintsDemoConfig.description,
185
+ Component: wrapSimpleDemo(portConstraintsDemoConfig),
186
+ },
187
+ {
188
+ id: portPositionLimitsDemoConfig.id,
189
+ title: portPositionLimitsDemoConfig.title,
190
+ description: portPositionLimitsDemoConfig.description,
191
+ Component: wrapSimpleDemo(portPositionLimitsDemoConfig),
192
+ },
193
+ {
194
+ id: labelStyleDemoConfig.id,
195
+ title: labelStyleDemoConfig.title,
196
+ description: labelStyleDemoConfig.description,
197
+ Component: wrapSimpleDemo(labelStyleDemoConfig),
198
+ },
199
+ {
200
+ id: linkColorPoolDemoConfig.id,
201
+ title: linkColorPoolDemoConfig.title,
202
+ description: linkColorPoolDemoConfig.description,
203
+ Component: LinkColorPoolDemo,
204
+ },
205
+ {
206
+ id: portBorderDemoConfig.id,
207
+ title: portBorderDemoConfig.title,
208
+ description: portBorderDemoConfig.description,
197
209
  Component: wrapSimpleDemo(portBorderDemoConfig),
198
210
  },
199
211
  {
@@ -202,18 +214,18 @@ export const demoTabs: DemoDefinition[] = [
202
214
  description: shapeBorderMovementDemoConfig.description,
203
215
  Component: wrapSimpleDemo(shapeBorderMovementDemoConfig),
204
216
  },
205
- {
206
- id: shapeHoverControlsDemoConfig.id,
207
- title: shapeHoverControlsDemoConfig.title,
208
- description: shapeHoverControlsDemoConfig.description,
209
- Component: ShapeHoverControlsDemo,
210
- },
211
- {
212
- id: vertexControlLinkSessionDemoConfig.id,
213
- title: vertexControlLinkSessionDemoConfig.title,
214
- description: vertexControlLinkSessionDemoConfig.description,
215
- Component: VertexControlLinkSessionDemo,
216
- },
217
+ {
218
+ id: shapeHoverControlsDemoConfig.id,
219
+ title: shapeHoverControlsDemoConfig.title,
220
+ description: shapeHoverControlsDemoConfig.description,
221
+ Component: ShapeHoverControlsDemo,
222
+ },
223
+ {
224
+ id: vertexControlLinkSessionDemoConfig.id,
225
+ title: vertexControlLinkSessionDemoConfig.title,
226
+ description: vertexControlLinkSessionDemoConfig.description,
227
+ Component: VertexControlLinkSessionDemo,
228
+ },
217
229
  {
218
230
  id: childConstraintsDemoConfig.id,
219
231
  title: childConstraintsDemoConfig.title,
@@ -238,9 +250,21 @@ export const demoTabs: DemoDefinition[] = [
238
250
  description: obstacleRoutingDemoConfig.description,
239
251
  Component: ObstacleRoutingDemo,
240
252
  },
241
- {
242
- id: linkBendHandlesDemoConfig.id,
243
- title: linkBendHandlesDemoConfig.title,
253
+ {
254
+ id: offsetAnchorAvoidanceDemoConfig.id,
255
+ title: offsetAnchorAvoidanceDemoConfig.title,
256
+ description: offsetAnchorAvoidanceDemoConfig.description,
257
+ Component: OffsetAnchorAvoidanceDemo,
258
+ },
259
+ {
260
+ id: zoomToFitElementsDemoConfig.id,
261
+ title: zoomToFitElementsDemoConfig.title,
262
+ description: zoomToFitElementsDemoConfig.description,
263
+ Component: ZoomToFitElementsDemo,
264
+ },
265
+ {
266
+ id: linkBendHandlesDemoConfig.id,
267
+ title: linkBendHandlesDemoConfig.title,
244
268
  description: linkBendHandlesDemoConfig.description,
245
269
  Component: wrapSimpleDemo(linkBendHandlesDemoConfig),
246
270
  },