orcasvn-react-diagrams 0.2.2 → 0.2.3
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 +51 -0
- package/README.md +11 -3
- package/dist/cjs/examples.js +1768 -161
- package/dist/cjs/index.js +786 -120
- package/dist/cjs/types/api/createDiagramEditor.d.ts +19 -1
- package/dist/cjs/types/api/index.d.ts +1 -1
- package/dist/cjs/types/api/types.d.ts +32 -0
- package/dist/cjs/types/displaybox/DisplayBoxControls.d.ts +5 -1
- package/dist/cjs/types/displaybox/demos/AsymmetricPortMultiAnchorDemoTab.d.ts +3 -0
- package/dist/cjs/types/displaybox/demos/LayoutLabelReservedSpaceDemoTab.d.ts +3 -0
- package/dist/cjs/types/displaybox/demos/VertexControlLinkSessionDemoTab.d.ts +3 -0
- package/dist/cjs/types/displaybox/demos/asymmetricPortMultiAnchorDemo.d.ts +31 -0
- package/dist/cjs/types/displaybox/demos/layoutLabelReservedSpaceDemo.d.ts +11 -0
- package/dist/cjs/types/displaybox/demos/vertexControlLinkSessionDemo.d.ts +12 -0
- package/dist/cjs/types/displaybox/useDemoControls.d.ts +4 -0
- package/dist/cjs/types/engine/AutoLayoutService.d.ts +2 -0
- package/dist/cjs/types/engine/DiagramEngine.d.ts +5 -0
- package/dist/cjs/types/engine/LinkRoutingService.d.ts +9 -1
- package/dist/cjs/types/models/PortModel.d.ts +5 -0
- package/dist/cjs/types/renderer/RenderTypes.d.ts +3 -1
- package/dist/cjs/types/renderer/konva/KonvaInteraction.d.ts +14 -0
- package/dist/cjs/types/renderer/konva/KonvaNodeFactory.d.ts +1 -0
- package/dist/cjs/types/renderer/konva/KonvaRenderer.d.ts +0 -1
- package/dist/cjs/types/shapes/BuiltInShapes.d.ts +3 -1
- package/dist/cjs/types/utils/__tests__/portGeometry.test.d.ts +1 -0
- package/dist/cjs/types/utils/portGeometry.d.ts +44 -0
- package/dist/esm/examples.js +1769 -162
- package/dist/esm/examples.js.map +1 -1
- package/dist/esm/index.js +786 -120
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/api/createDiagramEditor.d.ts +19 -1
- package/dist/esm/types/api/index.d.ts +1 -1
- package/dist/esm/types/api/types.d.ts +32 -0
- package/dist/esm/types/displaybox/DisplayBoxControls.d.ts +5 -1
- package/dist/esm/types/displaybox/demos/AsymmetricPortMultiAnchorDemoTab.d.ts +3 -0
- package/dist/esm/types/displaybox/demos/LayoutLabelReservedSpaceDemoTab.d.ts +3 -0
- package/dist/esm/types/displaybox/demos/VertexControlLinkSessionDemoTab.d.ts +3 -0
- package/dist/esm/types/displaybox/demos/asymmetricPortMultiAnchorDemo.d.ts +31 -0
- package/dist/esm/types/displaybox/demos/layoutLabelReservedSpaceDemo.d.ts +11 -0
- package/dist/esm/types/displaybox/demos/vertexControlLinkSessionDemo.d.ts +12 -0
- package/dist/esm/types/displaybox/useDemoControls.d.ts +4 -0
- package/dist/esm/types/engine/AutoLayoutService.d.ts +2 -0
- package/dist/esm/types/engine/DiagramEngine.d.ts +5 -0
- package/dist/esm/types/engine/LinkRoutingService.d.ts +9 -1
- package/dist/esm/types/models/PortModel.d.ts +5 -0
- package/dist/esm/types/renderer/RenderTypes.d.ts +3 -1
- package/dist/esm/types/renderer/konva/KonvaInteraction.d.ts +14 -0
- package/dist/esm/types/renderer/konva/KonvaNodeFactory.d.ts +1 -0
- package/dist/esm/types/renderer/konva/KonvaRenderer.d.ts +0 -1
- package/dist/esm/types/shapes/BuiltInShapes.d.ts +3 -1
- package/dist/esm/types/utils/__tests__/portGeometry.test.d.ts +1 -0
- package/dist/esm/types/utils/portGeometry.d.ts +44 -0
- package/dist/examples.d.ts +50 -0
- package/dist/index.d.ts +58 -1
- package/package.json +11 -10
- package/src/displaybox/demos/AsymmetricPortMultiAnchorDemoTab.tsx +269 -0
- package/src/displaybox/demos/AutoLayoutDemoTab.tsx +113 -11
- package/src/displaybox/demos/DeletionEventsDemoTab.tsx +6 -1
- package/src/displaybox/demos/EngineEventsDemoTab.tsx +5 -0
- package/src/displaybox/demos/EventHandlersDemoTab.tsx +5 -0
- package/src/displaybox/demos/ExternalDragDropDemoTab.tsx +5 -0
- package/src/displaybox/demos/LayoutLabelReservedSpaceDemoTab.tsx +291 -0
- package/src/displaybox/demos/LinkCancelDemoTab.tsx +5 -0
- package/src/displaybox/demos/ShapeHoverControlsDemoTab.tsx +6 -1
- package/src/displaybox/demos/SimpleDemo.tsx +5 -0
- package/src/displaybox/demos/SvgPathDemoTab.tsx +5 -0
- package/src/displaybox/demos/TextLayoutDemoTab.tsx +6 -1
- package/src/displaybox/demos/VertexControlLinkSessionDemoTab.tsx +302 -0
- package/src/displaybox/demos/asymmetricPortMultiAnchorDemo.ts +357 -0
- package/src/displaybox/demos/autoLayoutDemo.ts +23 -5
- package/src/displaybox/demos/index.tsx +91 -75
- package/src/displaybox/demos/layoutLabelReservedSpaceDemo.ts +121 -0
- package/src/displaybox/demos/vertexControlLinkSessionDemo.ts +145 -0
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ type DiagramContainer = {
|
|
|
22
22
|
type MoveConstraint = 'free' | 'inside' | 'border';
|
|
23
23
|
type BorderSide$1 = 'left' | 'right' | 'top' | 'bottom';
|
|
24
24
|
type HostAnchorPreset = 'vertices' | 'cardinal';
|
|
25
|
+
type PortLinkAttachMode = 'external' | 'internal';
|
|
25
26
|
type PortAnchor = {
|
|
26
27
|
id: string;
|
|
27
28
|
position: Point;
|
|
@@ -29,6 +30,20 @@ type PortAnchor = {
|
|
|
29
30
|
normal?: Point;
|
|
30
31
|
meta?: Record<string, unknown>;
|
|
31
32
|
};
|
|
33
|
+
type PortBorderTransformContext = {
|
|
34
|
+
side: BorderSide$1;
|
|
35
|
+
normal: Point;
|
|
36
|
+
hostRect: Rect;
|
|
37
|
+
attachMode: PortLinkAttachMode;
|
|
38
|
+
effectiveLinkAttachPoint: Point;
|
|
39
|
+
placementPoint: Point;
|
|
40
|
+
rotationPivot: Point;
|
|
41
|
+
portSize?: Size;
|
|
42
|
+
};
|
|
43
|
+
type PortBorderTransformResult = {
|
|
44
|
+
rotation?: number;
|
|
45
|
+
offset?: Point;
|
|
46
|
+
};
|
|
32
47
|
type PortAnchorConstraint = {
|
|
33
48
|
preset: HostAnchorPreset;
|
|
34
49
|
fallback?: 'nearest';
|
|
@@ -153,6 +168,15 @@ type ElementLayoutMode = 'manual' | 'horizontal' | 'vertical';
|
|
|
153
168
|
type ElementLayoutAlign = 'start' | 'center' | 'end';
|
|
154
169
|
type ElementLayoutChildFitMainAxis = 'none' | 'distribute';
|
|
155
170
|
type ElementLayoutChildFitCrossAxis = 'none' | 'stretch';
|
|
171
|
+
type ElementLayoutLabelReservedSpaceMode = 'none' | 'fixed' | 'flexible';
|
|
172
|
+
type ElementLayoutLabelReservedSpacePlacement = 'top';
|
|
173
|
+
type ElementLayoutLabelReservedSpace = {
|
|
174
|
+
mode?: ElementLayoutLabelReservedSpaceMode;
|
|
175
|
+
placement?: ElementLayoutLabelReservedSpacePlacement;
|
|
176
|
+
size?: number;
|
|
177
|
+
minSize?: number;
|
|
178
|
+
maxSize?: number;
|
|
179
|
+
};
|
|
156
180
|
type TextLayoutBoundsMode = 'owner-width' | 'owner-box' | 'fixed';
|
|
157
181
|
type TextLayoutWrapMode = 'none' | 'word' | 'char';
|
|
158
182
|
type TextLayoutOverflowMode = 'clip' | 'ellipsis-end' | 'ellipsis-middle' | 'ellipsis-start';
|
|
@@ -168,6 +192,7 @@ type ElementLayout = {
|
|
|
168
192
|
childFitCrossAxis?: ElementLayoutChildFitCrossAxis;
|
|
169
193
|
childFitMinSize?: Partial<Size>;
|
|
170
194
|
childFitMaxSize?: Partial<Size>;
|
|
195
|
+
labelReservedSpace?: ElementLayoutLabelReservedSpace;
|
|
171
196
|
};
|
|
172
197
|
type TextLayout = {
|
|
173
198
|
boundsMode?: TextLayoutBoundsMode;
|
|
@@ -222,6 +247,11 @@ type PortData = {
|
|
|
222
247
|
moveMode?: MoveConstraint;
|
|
223
248
|
anchorCenter?: boolean;
|
|
224
249
|
orientToHostBorder?: boolean;
|
|
250
|
+
placementPoint?: Point;
|
|
251
|
+
linkAttachPoint?: Point;
|
|
252
|
+
externalLinkAttachPoint?: Point;
|
|
253
|
+
internalLinkAttachPoint?: Point;
|
|
254
|
+
rotationPivot?: Point;
|
|
225
255
|
currentAnchorId?: string;
|
|
226
256
|
};
|
|
227
257
|
type ShapeDrawContext = {
|
|
@@ -441,7 +471,9 @@ type DiagramEngineHandle = {
|
|
|
441
471
|
svgSize?: Size;
|
|
442
472
|
projectToBorder?: (point: Point, rect: Rect) => Point;
|
|
443
473
|
resolveBorderSide?: (point: Point, rect: Rect) => BorderSide$1;
|
|
474
|
+
resolveBorderNormal?: (point: Point, rect: Rect) => Point;
|
|
444
475
|
resolvePortAnchors?: (rect: Rect, options: ResolvePortAnchorsOptions) => PortAnchor[];
|
|
476
|
+
resolvePortBorderTransform?: (context: PortBorderTransformContext) => PortBorderTransformResult | undefined;
|
|
445
477
|
resolveHoverGeometry?: (rect: Rect) => ShapeHoverGeometry | undefined;
|
|
446
478
|
resolveEllipseMidPoints?: (rect: Rect) => ShapeEllipseMidPointTarget[] | undefined;
|
|
447
479
|
}) => void;
|
|
@@ -506,6 +538,11 @@ declare class PortModel {
|
|
|
506
538
|
moveMode?: MoveConstraint;
|
|
507
539
|
anchorCenter?: boolean;
|
|
508
540
|
orientToHostBorder: boolean;
|
|
541
|
+
placementPoint?: Point;
|
|
542
|
+
linkAttachPoint?: Point;
|
|
543
|
+
externalLinkAttachPoint?: Point;
|
|
544
|
+
internalLinkAttachPoint?: Point;
|
|
545
|
+
rotationPivot?: Point;
|
|
509
546
|
currentAnchorId?: string;
|
|
510
547
|
constructor(data: PortData);
|
|
511
548
|
setPosition(position: Point): void;
|
|
@@ -657,7 +694,9 @@ type ShapeDefinition = {
|
|
|
657
694
|
};
|
|
658
695
|
projectToBorder?: (point: Point, rect: Rect) => Point;
|
|
659
696
|
resolveBorderSide?: (point: Point, rect: Rect) => BorderSide;
|
|
697
|
+
resolveBorderNormal?: (point: Point, rect: Rect) => Point;
|
|
660
698
|
resolvePortAnchors?: (rect: Rect, options: ResolvePortAnchorsOptions) => PortAnchor[];
|
|
699
|
+
resolvePortBorderTransform?: (context: PortBorderTransformContext) => PortBorderTransformResult | undefined;
|
|
661
700
|
resolveHoverGeometry?: (rect: Rect) => ShapeHoverGeometry | undefined;
|
|
662
701
|
resolveEllipseMidPoints?: (rect: Rect) => ShapeEllipseMidPointTarget[] | undefined;
|
|
663
702
|
};
|
|
@@ -703,11 +742,29 @@ type DiagramEditorConfig = {
|
|
|
703
742
|
onChange?: (event: EngineChangeEvent) => void;
|
|
704
743
|
onSelection?: (event: EngineSelectionEvent) => void;
|
|
705
744
|
};
|
|
745
|
+
type DiagramImageExportOptions = {
|
|
746
|
+
mimeType?: string;
|
|
747
|
+
quality?: number;
|
|
748
|
+
pixelRatio?: number;
|
|
749
|
+
x?: number;
|
|
750
|
+
y?: number;
|
|
751
|
+
width?: number;
|
|
752
|
+
height?: number;
|
|
753
|
+
fitToContent?: boolean | {
|
|
754
|
+
padding?: number;
|
|
755
|
+
};
|
|
756
|
+
};
|
|
706
757
|
type DiagramEditorHandle = DiagramEngineHandle & {
|
|
758
|
+
startLinkFromPort: (sourcePortId: string, pointer?: Point) => void;
|
|
759
|
+
updateLinkPreview: (pointer: Point) => void;
|
|
760
|
+
completeLinkToPort: (targetPortId: string) => void;
|
|
761
|
+
completeLinkToElement: (targetElementId: string, pointer: Point) => void;
|
|
762
|
+
cancelLink: () => void;
|
|
763
|
+
exportImage: (options?: DiagramImageExportOptions) => string;
|
|
707
764
|
resize: (width: number, height: number) => void;
|
|
708
765
|
setElementShapeHoverControls: (controls?: ElementShapeHoverControls) => void;
|
|
709
766
|
destroy: () => void;
|
|
710
767
|
};
|
|
711
768
|
declare const createDiagramEditor: (config: DiagramEditorConfig) => DiagramEditorHandle;
|
|
712
769
|
|
|
713
|
-
export { type AnchorReference, type BorderSide$1 as BorderSide, type ClientRectLike, type DiagramContainer, type DiagramEditorConfig, type DiagramEditorHandle, type DiagramEngineHandle, type DiagramPatch, type DiagramState, type EdgeHoverControl, type ElementData, type ElementDeletedEvent, type ElementDropEvent, type ElementLayout, type ElementLayoutAlign, type ElementLayoutChildFitCrossAxis, type ElementLayoutChildFitMainAxis, 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 PortData, type PortDeletedEvent, 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 };
|
|
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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orcasvn-react-diagrams",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"eventemitter3": "^5.0.1",
|
|
6
6
|
"flatten-js": "^0.6.9",
|
|
@@ -57,15 +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
|
-
"
|
|
67
|
-
"
|
|
68
|
-
|
|
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
|
+
],
|
|
69
70
|
"types": "dist/index.d.ts",
|
|
70
71
|
"devDependencies": {
|
|
71
72
|
"@rollup/plugin-commonjs": "^26.0.1",
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import type { OverlayShapeHandle, PortData } from '../../api';
|
|
3
|
+
import DisplayBoxControls from '../DisplayBoxControls';
|
|
4
|
+
import DisplayBoxStage from '../DisplayBoxStage';
|
|
5
|
+
import useDemoControls from '../useDemoControls';
|
|
6
|
+
import useDemoEditor from '../useDemoEditor';
|
|
7
|
+
import useOffsetSequence from '../useOffsetSequence';
|
|
8
|
+
import type { DemoActionHelpers } from '../types';
|
|
9
|
+
import {
|
|
10
|
+
asymmetricPortDefaultVariantId,
|
|
11
|
+
asymmetricPortMultiAnchorDemoConfig,
|
|
12
|
+
asymmetricPortShapeVariants,
|
|
13
|
+
createAsymmetricPortMultiAnchorState,
|
|
14
|
+
multiAnchorExternalPortId,
|
|
15
|
+
multiAnchorHostId,
|
|
16
|
+
resolveAsymmetricPortShapeVariant,
|
|
17
|
+
} from './asymmetricPortMultiAnchorDemo';
|
|
18
|
+
|
|
19
|
+
type DemoSide = 'top' | 'right' | 'bottom' | 'left';
|
|
20
|
+
|
|
21
|
+
const sidePositions: Record<DemoSide, { x: number; y: number }> = {
|
|
22
|
+
top: { x: 112, y: 0 },
|
|
23
|
+
right: { x: 280, y: 68 },
|
|
24
|
+
bottom: { x: 182, y: 180 },
|
|
25
|
+
left: { x: 0, y: 130 },
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const sideLabels: DemoSide[] = ['top', 'right', 'bottom', 'left'];
|
|
29
|
+
const resolvePortSide = (port: PortData, host: { size: { width: number; height: number } }): DemoSide | null => {
|
|
30
|
+
if (Math.abs(port.position.x) <= 0.001) return 'left';
|
|
31
|
+
if (Math.abs(port.position.x - host.size.width) <= 0.001) return 'right';
|
|
32
|
+
if (Math.abs(port.position.y) <= 0.001) return 'top';
|
|
33
|
+
if (Math.abs(port.position.y - host.size.height) <= 0.001) return 'bottom';
|
|
34
|
+
return null;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const sideToVector = (side: DemoSide): { x: number; y: number } => {
|
|
38
|
+
if (side === 'left') return { x: -1, y: 0 };
|
|
39
|
+
if (side === 'right') return { x: 1, y: 0 };
|
|
40
|
+
if (side === 'top') return { x: 0, y: -1 };
|
|
41
|
+
return { x: 0, y: 1 };
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const AsymmetricPortMultiAnchorDemo = () => {
|
|
45
|
+
const demo = asymmetricPortMultiAnchorDemoConfig;
|
|
46
|
+
const [showLegacyComparison, setShowLegacyComparison] = useState(true);
|
|
47
|
+
const [showExpectedAnchors, setShowExpectedAnchors] = useState(false);
|
|
48
|
+
const [selectedVariantId, setSelectedVariantId] = useState(asymmetricPortDefaultVariantId);
|
|
49
|
+
const overlayHandlesRef = useRef<OverlayShapeHandle[]>([]);
|
|
50
|
+
const selectedVariant = useMemo(
|
|
51
|
+
() => resolveAsymmetricPortShapeVariant(selectedVariantId),
|
|
52
|
+
[selectedVariantId],
|
|
53
|
+
);
|
|
54
|
+
const attachDistance = selectedVariant.externalLinkAttachPoint.x - selectedVariant.placementPoint.x;
|
|
55
|
+
const createState = useCallback(
|
|
56
|
+
() => createAsymmetricPortMultiAnchorState(showLegacyComparison, selectedVariantId),
|
|
57
|
+
[showLegacyComparison, selectedVariantId],
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const { containerRef, editorRef, diagramState, selection, snapEnabled, setSnapEnabled } = useDemoEditor({
|
|
61
|
+
createState,
|
|
62
|
+
elementShapes: demo.elementShapes,
|
|
63
|
+
portShapes: demo.portShapes,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const nextOffset = useOffsetSequence();
|
|
67
|
+
const actionHelpers: DemoActionHelpers = useMemo(() => ({ nextOffset }), [nextOffset]);
|
|
68
|
+
const controls = useDemoControls({
|
|
69
|
+
demo,
|
|
70
|
+
editorRef,
|
|
71
|
+
diagramState,
|
|
72
|
+
selection,
|
|
73
|
+
snapEnabled,
|
|
74
|
+
setSnapEnabled,
|
|
75
|
+
actionHelpers,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const editor = editorRef.current;
|
|
80
|
+
if (!editor) return;
|
|
81
|
+
asymmetricPortShapeVariants.forEach((variant) => {
|
|
82
|
+
editor.registerShape({
|
|
83
|
+
id: variant.shapeId,
|
|
84
|
+
baseRotation: 90,
|
|
85
|
+
svgPath: variant.svgPath,
|
|
86
|
+
svgSize: variant.svgSize,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
editor.rerouteAllLinks();
|
|
90
|
+
}, [editorRef, showLegacyComparison, selectedVariantId]);
|
|
91
|
+
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
const editor = editorRef.current;
|
|
94
|
+
const state = diagramState;
|
|
95
|
+
|
|
96
|
+
overlayHandlesRef.current.forEach((handle) => handle.destroy());
|
|
97
|
+
overlayHandlesRef.current = [];
|
|
98
|
+
|
|
99
|
+
if (!showExpectedAnchors || !editor || !state) return;
|
|
100
|
+
|
|
101
|
+
const hosts = new Map(state.elements.map((element) => [element.id, element]));
|
|
102
|
+
const debugPorts = state.ports.filter((port) => port.id.startsWith('multi-anchor-port-'));
|
|
103
|
+
|
|
104
|
+
debugPorts.forEach((port) => {
|
|
105
|
+
const host = hosts.get(port.elementId);
|
|
106
|
+
if (!host) return;
|
|
107
|
+
const side = resolvePortSide(port, host);
|
|
108
|
+
if (!side) return;
|
|
109
|
+
const vector = sideToVector(side);
|
|
110
|
+
const placementWorld = {
|
|
111
|
+
x: host.position.x + port.position.x,
|
|
112
|
+
y: host.position.y + port.position.y,
|
|
113
|
+
};
|
|
114
|
+
overlayHandlesRef.current.push(
|
|
115
|
+
editor.createOverlayShape({
|
|
116
|
+
shapeId: 'default',
|
|
117
|
+
position: placementWorld,
|
|
118
|
+
size: { width: 8, height: 8 },
|
|
119
|
+
anchorCenter: true,
|
|
120
|
+
style: {
|
|
121
|
+
fill: '#ffffff',
|
|
122
|
+
stroke: '#d97706',
|
|
123
|
+
strokeWidth: 2,
|
|
124
|
+
cornerRadius: 1,
|
|
125
|
+
opacity: 0.9,
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
if (port.id === multiAnchorExternalPortId || port.id.endsWith('-port-top') || port.id.endsWith('-port-bottom')) {
|
|
131
|
+
overlayHandlesRef.current.push(
|
|
132
|
+
editor.createOverlayShape({
|
|
133
|
+
shapeId: 'port-dark',
|
|
134
|
+
position: {
|
|
135
|
+
x: placementWorld.x + vector.x * attachDistance,
|
|
136
|
+
y: placementWorld.y + vector.y * attachDistance,
|
|
137
|
+
},
|
|
138
|
+
size: { width: 8, height: 8 },
|
|
139
|
+
anchorCenter: true,
|
|
140
|
+
style: {
|
|
141
|
+
fill: '#dc2626',
|
|
142
|
+
stroke: '#dc2626',
|
|
143
|
+
opacity: 0.85,
|
|
144
|
+
},
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return () => {
|
|
151
|
+
overlayHandlesRef.current.forEach((handle) => handle.destroy());
|
|
152
|
+
overlayHandlesRef.current = [];
|
|
153
|
+
};
|
|
154
|
+
}, [diagramState, editorRef, showExpectedAnchors]);
|
|
155
|
+
|
|
156
|
+
const moveExternalPortToSide = (side: DemoSide) => {
|
|
157
|
+
const editor = editorRef.current;
|
|
158
|
+
const host = diagramState?.elements.find((element) => element.id === multiAnchorHostId);
|
|
159
|
+
if (!editor || !host) return;
|
|
160
|
+
const local = sidePositions[side];
|
|
161
|
+
editor.movePortTo(multiAnchorExternalPortId, host.position.x + local.x, host.position.y + local.y);
|
|
162
|
+
editor.setSelection([multiAnchorExternalPortId]);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<section>
|
|
167
|
+
<div style={{ marginBottom: 12 }}>
|
|
168
|
+
<h2 style={{ marginTop: 0, marginBottom: 4 }}>{demo.title}</h2>
|
|
169
|
+
<p style={{ marginTop: 0, marginBottom: 8 }}>{demo.description}</p>
|
|
170
|
+
<p style={{ marginTop: 0, marginBottom: 0, fontSize: 13, color: '#475569' }}>
|
|
171
|
+
The custom `svgPath` glyph is intentionally non-centered. The multi-anchor row keeps the square pivot pinned to the
|
|
172
|
+
border while links use the circle for external targets and the square pivot for parent-child internal links.
|
|
173
|
+
</p>
|
|
174
|
+
<p style={{ marginTop: 8, marginBottom: 0, fontSize: 13, color: '#475569' }}>
|
|
175
|
+
Active glyph variant: <strong>{selectedVariant.label}</strong> ({selectedVariant.description})
|
|
176
|
+
</p>
|
|
177
|
+
</div>
|
|
178
|
+
<DisplayBoxControls
|
|
179
|
+
actions={demo.actions}
|
|
180
|
+
snapEnabled={controls.snapEnabled}
|
|
181
|
+
selectedLinkRouting={controls.selectedLinkRouting}
|
|
182
|
+
canToggleLinkRouting={controls.canToggleLinkRouting}
|
|
183
|
+
onReload={controls.handleReload}
|
|
184
|
+
onZoomIn={controls.handleZoomIn}
|
|
185
|
+
onZoomOut={controls.handleZoomOut}
|
|
186
|
+
onResetViewport={controls.handleResetViewport}
|
|
187
|
+
onToggleSnap={controls.handleToggleSnap}
|
|
188
|
+
onManualRender={controls.handleManualRender}
|
|
189
|
+
onToggleLinkRouting={controls.handleToggleLinkRouting}
|
|
190
|
+
onAction={controls.handleAction}
|
|
191
|
+
onExportImage={controls.handleExportImage}
|
|
192
|
+
onClearExportPreview={controls.handleClearExportPreview}
|
|
193
|
+
exportPreviewDataUrl={controls.exportPreviewDataUrl}
|
|
194
|
+
exportError={controls.exportError}
|
|
195
|
+
/>
|
|
196
|
+
<div
|
|
197
|
+
style={{
|
|
198
|
+
display: 'grid',
|
|
199
|
+
gridTemplateColumns: 'minmax(0, 1fr) auto',
|
|
200
|
+
gap: 12,
|
|
201
|
+
alignItems: 'start',
|
|
202
|
+
marginBottom: 12,
|
|
203
|
+
padding: 12,
|
|
204
|
+
border: '1px solid #d9e2f0',
|
|
205
|
+
borderRadius: 8,
|
|
206
|
+
background: '#f8fbff',
|
|
207
|
+
}}
|
|
208
|
+
>
|
|
209
|
+
<div style={{ display: 'grid', gap: 8 }}>
|
|
210
|
+
<label htmlFor="legacy-comparison-toggle" style={{ display: 'flex', alignItems: 'center', gap: 8, fontWeight: 600 }}>
|
|
211
|
+
<input
|
|
212
|
+
id="legacy-comparison-toggle"
|
|
213
|
+
type="checkbox"
|
|
214
|
+
checked={showLegacyComparison}
|
|
215
|
+
onChange={(event) => setShowLegacyComparison(event.target.checked)}
|
|
216
|
+
/>
|
|
217
|
+
Show legacy/default comparison
|
|
218
|
+
</label>
|
|
219
|
+
<label htmlFor="expected-anchor-toggle" style={{ display: 'flex', alignItems: 'center', gap: 8, fontWeight: 600 }}>
|
|
220
|
+
<input
|
|
221
|
+
id="expected-anchor-toggle"
|
|
222
|
+
type="checkbox"
|
|
223
|
+
checked={showExpectedAnchors}
|
|
224
|
+
onChange={(event) => setShowExpectedAnchors(event.target.checked)}
|
|
225
|
+
/>
|
|
226
|
+
Show expected anchor markers
|
|
227
|
+
</label>
|
|
228
|
+
<label htmlFor="asymmetric-variant-select" style={{ display: 'flex', alignItems: 'center', gap: 8, fontWeight: 600 }}>
|
|
229
|
+
Port glyph variant
|
|
230
|
+
<select
|
|
231
|
+
id="asymmetric-variant-select"
|
|
232
|
+
value={selectedVariantId}
|
|
233
|
+
onChange={(event) => setSelectedVariantId(event.target.value)}
|
|
234
|
+
>
|
|
235
|
+
{asymmetricPortShapeVariants.map((variant) => (
|
|
236
|
+
<option key={variant.id} value={variant.id}>
|
|
237
|
+
{variant.label}
|
|
238
|
+
</option>
|
|
239
|
+
))}
|
|
240
|
+
</select>
|
|
241
|
+
</label>
|
|
242
|
+
<div style={{ fontSize: 13, color: '#334155' }}>
|
|
243
|
+
Drag any asymmetric port around the host border or use the side controls to verify top/right/bottom/left orientation.
|
|
244
|
+
</div>
|
|
245
|
+
<div style={{ fontSize: 13, color: '#334155' }}>
|
|
246
|
+
Quick visual check: the multi-anchor row should keep the square touching the border; the legacy row may shift because
|
|
247
|
+
it has no explicit placement or pivot anchors.
|
|
248
|
+
</div>
|
|
249
|
+
<div style={{ fontSize: 13, color: '#334155' }}>
|
|
250
|
+
Debug markers: amber square = expected border placement point, red dot = expected external attach point.
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
<div style={{ display: 'grid', gap: 8 }}>
|
|
254
|
+
<div style={{ fontSize: 12, fontWeight: 600, color: '#1e293b' }}>Move the external demo port</div>
|
|
255
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, justifyContent: 'flex-end' }}>
|
|
256
|
+
{sideLabels.map((side) => (
|
|
257
|
+
<button key={side} type="button" onClick={() => moveExternalPortToSide(side)} style={{ padding: '6px 10px' }}>
|
|
258
|
+
External → {side[0].toUpperCase() + side.slice(1)}
|
|
259
|
+
</button>
|
|
260
|
+
))}
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
<DisplayBoxStage containerRef={containerRef} />
|
|
265
|
+
</section>
|
|
266
|
+
);
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
export default AsymmetricPortMultiAnchorDemo;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import type { ElementLayout, ElementLayoutMode } from '../../api';
|
|
2
|
+
import type { ElementLayout, ElementLayoutLabelReservedSpaceMode, ElementLayoutMode } from '../../api';
|
|
3
3
|
import { createId } from '../../utils/ids';
|
|
4
4
|
import DisplayBoxControls from '../DisplayBoxControls';
|
|
5
5
|
import DisplayBoxStage from '../DisplayBoxStage';
|
|
@@ -10,12 +10,15 @@ import type { DemoActionHelpers } from '../types';
|
|
|
10
10
|
import { autoLayoutDemoConfig } from './autoLayoutDemo';
|
|
11
11
|
import { gridStageStyle } from './shared';
|
|
12
12
|
|
|
13
|
-
const parentOptions = [
|
|
14
|
-
{ id: 'layout-row', label: 'Horizontal layout' },
|
|
15
|
-
{ id: 'layout-column', label: 'Vertical layout' },
|
|
16
|
-
{ id: 'layout-nested', label: 'Nested layout' },
|
|
17
|
-
{ id: 'layout-manual', label: 'Manual (compare)' },
|
|
18
|
-
];
|
|
13
|
+
const parentOptions = [
|
|
14
|
+
{ id: 'layout-row', label: 'Horizontal layout' },
|
|
15
|
+
{ id: 'layout-column', label: 'Vertical layout' },
|
|
16
|
+
{ id: 'layout-nested', label: 'Nested layout' },
|
|
17
|
+
{ id: 'layout-manual', label: 'Manual (compare)' },
|
|
18
|
+
];
|
|
19
|
+
const shortLabel = 'Parent label lane demo';
|
|
20
|
+
const longLabel =
|
|
21
|
+
'Parent label lane demo with longer content to increase flexible reserved space and push children downward.';
|
|
19
22
|
|
|
20
23
|
const AutoLayoutDemo = () => {
|
|
21
24
|
const demo = autoLayoutDemoConfig;
|
|
@@ -49,6 +52,10 @@ const AutoLayoutDemo = () => {
|
|
|
49
52
|
const [childFitMinHeight, setChildFitMinHeight] = useState<number | ''>('');
|
|
50
53
|
const [childFitMaxWidth, setChildFitMaxWidth] = useState<number | ''>('');
|
|
51
54
|
const [childFitMaxHeight, setChildFitMaxHeight] = useState<number | ''>('');
|
|
55
|
+
const [labelReservedMode, setLabelReservedMode] = useState<ElementLayoutLabelReservedSpaceMode>('none');
|
|
56
|
+
const [labelReservedFixedSize, setLabelReservedFixedSize] = useState<number>(32);
|
|
57
|
+
const [labelReservedMinSize, setLabelReservedMinSize] = useState<number | ''>('');
|
|
58
|
+
const [labelReservedMaxSize, setLabelReservedMaxSize] = useState<number | ''>('');
|
|
52
59
|
const [lastTrigger, setLastTrigger] = useState<string>('None yet');
|
|
53
60
|
|
|
54
61
|
useEffect(() => {
|
|
@@ -133,6 +140,10 @@ const AutoLayoutDemo = () => {
|
|
|
133
140
|
setChildFitMinHeight(layout?.childFitMinSize?.height ?? '');
|
|
134
141
|
setChildFitMaxWidth(layout?.childFitMaxSize?.width ?? '');
|
|
135
142
|
setChildFitMaxHeight(layout?.childFitMaxSize?.height ?? '');
|
|
143
|
+
setLabelReservedMode(layout?.labelReservedSpace?.mode ?? 'none');
|
|
144
|
+
setLabelReservedFixedSize(layout?.labelReservedSpace?.size ?? 32);
|
|
145
|
+
setLabelReservedMinSize(layout?.labelReservedSpace?.minSize ?? '');
|
|
146
|
+
setLabelReservedMaxSize(layout?.labelReservedSpace?.maxSize ?? '');
|
|
136
147
|
}, [diagramState, targetId]);
|
|
137
148
|
|
|
138
149
|
const childOrder = useMemo(() => {
|
|
@@ -161,7 +172,7 @@ const AutoLayoutDemo = () => {
|
|
|
161
172
|
});
|
|
162
173
|
}, [diagramState]);
|
|
163
174
|
|
|
164
|
-
const handleApplyLayout = () => {
|
|
175
|
+
const handleApplyLayout = () => {
|
|
165
176
|
const editor = editorRef.current;
|
|
166
177
|
if (!editor) return;
|
|
167
178
|
if (!targetElement) return;
|
|
@@ -179,6 +190,16 @@ const AutoLayoutDemo = () => {
|
|
|
179
190
|
...(childFitMaxWidth === '' ? {} : { width: childFitMaxWidth }),
|
|
180
191
|
...(childFitMaxHeight === '' ? {} : { height: childFitMaxHeight }),
|
|
181
192
|
};
|
|
193
|
+
const labelReservedSpace =
|
|
194
|
+
mode === 'manual'
|
|
195
|
+
? undefined
|
|
196
|
+
: {
|
|
197
|
+
mode: labelReservedMode,
|
|
198
|
+
placement: 'top' as const,
|
|
199
|
+
...(labelReservedMode === 'fixed' ? { size: labelReservedFixedSize } : {}),
|
|
200
|
+
...(labelReservedMinSize === '' ? {} : { minSize: labelReservedMinSize }),
|
|
201
|
+
...(labelReservedMaxSize === '' ? {} : { maxSize: labelReservedMaxSize }),
|
|
202
|
+
};
|
|
182
203
|
const layout =
|
|
183
204
|
mode === 'manual'
|
|
184
205
|
? { mode: 'manual' as const }
|
|
@@ -191,10 +212,20 @@ const AutoLayoutDemo = () => {
|
|
|
191
212
|
childFitCrossAxis,
|
|
192
213
|
childFitMinSize,
|
|
193
214
|
childFitMaxSize,
|
|
215
|
+
labelReservedSpace,
|
|
194
216
|
};
|
|
195
217
|
editor.setElementLayout(targetElement.id, layout);
|
|
196
218
|
setLastTrigger(`layout applied (${mode})`);
|
|
197
219
|
};
|
|
220
|
+
|
|
221
|
+
const handleSetLabelContent = (content: string) => {
|
|
222
|
+
const editor = editorRef.current;
|
|
223
|
+
if (!editor || !diagramState || !targetElement) return;
|
|
224
|
+
const label = diagramState.texts.find((text) => text.ownerId === targetElement.id);
|
|
225
|
+
if (!label) return;
|
|
226
|
+
editor.updateText(label.id, content);
|
|
227
|
+
setLastTrigger('label updated');
|
|
228
|
+
};
|
|
198
229
|
|
|
199
230
|
const handleAddChild = () => {
|
|
200
231
|
const editor = editorRef.current;
|
|
@@ -250,6 +281,10 @@ const AutoLayoutDemo = () => {
|
|
|
250
281
|
onManualRender={controls.handleManualRender}
|
|
251
282
|
onToggleLinkRouting={controls.handleToggleLinkRouting}
|
|
252
283
|
onAction={controls.handleAction}
|
|
284
|
+
onExportImage={controls.handleExportImage}
|
|
285
|
+
onClearExportPreview={controls.handleClearExportPreview}
|
|
286
|
+
exportPreviewDataUrl={controls.exportPreviewDataUrl}
|
|
287
|
+
exportError={controls.exportError}
|
|
253
288
|
/>
|
|
254
289
|
|
|
255
290
|
<div style={{ display: 'grid', gap: 12, marginBottom: 12 }}>
|
|
@@ -400,6 +435,55 @@ const AutoLayoutDemo = () => {
|
|
|
400
435
|
/>
|
|
401
436
|
</div>
|
|
402
437
|
|
|
438
|
+
<label htmlFor="label-reserved-mode-select" style={{ fontWeight: 600 }}>
|
|
439
|
+
Label lane
|
|
440
|
+
</label>
|
|
441
|
+
<select
|
|
442
|
+
id="label-reserved-mode-select"
|
|
443
|
+
value={labelReservedMode}
|
|
444
|
+
onChange={(event) => setLabelReservedMode(event.target.value as ElementLayoutLabelReservedSpaceMode)}
|
|
445
|
+
style={{ padding: '6px 10px', minWidth: 120 }}
|
|
446
|
+
disabled={mode === 'manual'}
|
|
447
|
+
>
|
|
448
|
+
<option value="none">None</option>
|
|
449
|
+
<option value="fixed">Fixed</option>
|
|
450
|
+
<option value="flexible">Flexible</option>
|
|
451
|
+
</select>
|
|
452
|
+
|
|
453
|
+
<label htmlFor="label-reserved-fixed-input" style={{ fontWeight: 600 }}>
|
|
454
|
+
Lane fixed/min/max
|
|
455
|
+
</label>
|
|
456
|
+
<div style={{ display: 'inline-flex', gap: 6 }}>
|
|
457
|
+
<input
|
|
458
|
+
id="label-reserved-fixed-input"
|
|
459
|
+
type="number"
|
|
460
|
+
value={labelReservedFixedSize}
|
|
461
|
+
onChange={(event) => setLabelReservedFixedSize(Math.max(0, Number(event.target.value) || 0))}
|
|
462
|
+
style={{ width: 70, padding: '6px 8px' }}
|
|
463
|
+
min={0}
|
|
464
|
+
placeholder="fixed"
|
|
465
|
+
disabled={mode === 'manual'}
|
|
466
|
+
/>
|
|
467
|
+
<input
|
|
468
|
+
type="number"
|
|
469
|
+
value={labelReservedMinSize}
|
|
470
|
+
onChange={(event) => setLabelReservedMinSize(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0))}
|
|
471
|
+
style={{ width: 64, padding: '6px 8px' }}
|
|
472
|
+
min={0}
|
|
473
|
+
placeholder="min"
|
|
474
|
+
disabled={mode === 'manual'}
|
|
475
|
+
/>
|
|
476
|
+
<input
|
|
477
|
+
type="number"
|
|
478
|
+
value={labelReservedMaxSize}
|
|
479
|
+
onChange={(event) => setLabelReservedMaxSize(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0))}
|
|
480
|
+
style={{ width: 64, padding: '6px 8px' }}
|
|
481
|
+
min={0}
|
|
482
|
+
placeholder="max"
|
|
483
|
+
disabled={mode === 'manual'}
|
|
484
|
+
/>
|
|
485
|
+
</div>
|
|
486
|
+
|
|
403
487
|
<button type="button" onClick={handleApplyLayout} style={{ padding: '6px 12px', fontWeight: 600 }}>
|
|
404
488
|
Apply layout
|
|
405
489
|
</button>
|
|
@@ -420,11 +504,27 @@ const AutoLayoutDemo = () => {
|
|
|
420
504
|
alignItems: 'center',
|
|
421
505
|
}}
|
|
422
506
|
>
|
|
423
|
-
<span style={{ fontWeight: 600 }}>Target:</span> {targetElement ? targetElement.id : '—'}
|
|
507
|
+
<span style={{ fontWeight: 600 }}>Target:</span> {targetElement ? targetElement.id : '—'}
|
|
424
508
|
<span style={{ width: 6, height: 6, borderRadius: '50%', background: '#1f4d99', display: 'inline-block' }} />
|
|
425
509
|
Last trigger: <strong>{lastTrigger}</strong>
|
|
426
|
-
</span>
|
|
427
|
-
|
|
510
|
+
</span>
|
|
511
|
+
<button
|
|
512
|
+
type="button"
|
|
513
|
+
onClick={() => handleSetLabelContent(shortLabel)}
|
|
514
|
+
style={{ padding: '6px 10px' }}
|
|
515
|
+
disabled={!targetElement || mode === 'manual'}
|
|
516
|
+
>
|
|
517
|
+
Label short
|
|
518
|
+
</button>
|
|
519
|
+
<button
|
|
520
|
+
type="button"
|
|
521
|
+
onClick={() => handleSetLabelContent(longLabel)}
|
|
522
|
+
style={{ padding: '6px 10px' }}
|
|
523
|
+
disabled={!targetElement || mode === 'manual'}
|
|
524
|
+
>
|
|
525
|
+
Label long
|
|
526
|
+
</button>
|
|
527
|
+
</div>
|
|
428
528
|
|
|
429
529
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
|
|
430
530
|
<div>
|
|
@@ -447,6 +547,7 @@ const AutoLayoutDemo = () => {
|
|
|
447
547
|
<ul style={{ marginTop: 0, paddingLeft: 18, fontSize: 13 }}>
|
|
448
548
|
<li>Horizontal: try center vs bottom alignment and larger padding.</li>
|
|
449
549
|
<li>Vertical: see parent grow taller as children stack.</li>
|
|
550
|
+
<li>Label lane: compare none/fixed/flexible and observe children start below the reserved lane.</li>
|
|
450
551
|
<li>Fit main-axis distribute fills inner layout space across siblings.</li>
|
|
451
552
|
<li>Fit cross-axis stretch extends children across inner cross axis.</li>
|
|
452
553
|
<li>Min/max fit guards cap distributed or stretched child sizes.</li>
|
|
@@ -499,3 +600,4 @@ const AutoLayoutDemo = () => {
|
|
|
499
600
|
};
|
|
500
601
|
|
|
501
602
|
export default AutoLayoutDemo;
|
|
603
|
+
|
|
@@ -103,7 +103,11 @@ const DeletionEventsDemo = () => {
|
|
|
103
103
|
onToggleSnap={controls.handleToggleSnap}
|
|
104
104
|
onManualRender={controls.handleManualRender}
|
|
105
105
|
onToggleLinkRouting={controls.handleToggleLinkRouting}
|
|
106
|
-
onAction={controls.handleAction}
|
|
106
|
+
onAction={controls.handleAction}
|
|
107
|
+
onExportImage={controls.handleExportImage}
|
|
108
|
+
onClearExportPreview={controls.handleClearExportPreview}
|
|
109
|
+
exportPreviewDataUrl={controls.exportPreviewDataUrl}
|
|
110
|
+
exportError={controls.exportError}
|
|
107
111
|
/>
|
|
108
112
|
<div style={{ display: 'grid', gap: 12, marginBottom: 12 }}>
|
|
109
113
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
|
|
@@ -145,3 +149,4 @@ const DeletionEventsDemo = () => {
|
|
|
145
149
|
};
|
|
146
150
|
|
|
147
151
|
export default DeletionEventsDemo;
|
|
152
|
+
|
|
@@ -95,6 +95,10 @@ const EngineEventsDemo = () => {
|
|
|
95
95
|
onManualRender={controls.handleManualRender}
|
|
96
96
|
onToggleLinkRouting={controls.handleToggleLinkRouting}
|
|
97
97
|
onAction={controls.handleAction}
|
|
98
|
+
onExportImage={controls.handleExportImage}
|
|
99
|
+
onClearExportPreview={controls.handleClearExportPreview}
|
|
100
|
+
exportPreviewDataUrl={controls.exportPreviewDataUrl}
|
|
101
|
+
exportError={controls.exportError}
|
|
98
102
|
/>
|
|
99
103
|
<div style={{ display: 'grid', gap: 12, marginBottom: 12 }}>
|
|
100
104
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
@@ -149,3 +153,4 @@ const EngineEventsDemo = () => {
|
|
|
149
153
|
};
|
|
150
154
|
|
|
151
155
|
export default EngineEventsDemo;
|
|
156
|
+
|