@procaaso/alphinity-ui-components 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/IMPLEMENTATION_GUIDE.md +1351 -0
- package/README.md +9 -22
- package/dist/index.cjs +162 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +162 -11
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.d.cts
CHANGED
|
@@ -447,6 +447,7 @@ interface NodeInstance {
|
|
|
447
447
|
x: number;
|
|
448
448
|
y: number;
|
|
449
449
|
};
|
|
450
|
+
labelFontSize?: number;
|
|
450
451
|
metadata?: Record<string, unknown>;
|
|
451
452
|
}
|
|
452
453
|
interface Transform {
|
|
@@ -541,6 +542,7 @@ interface DiagramMetadata {
|
|
|
541
542
|
modified?: string;
|
|
542
543
|
tags?: string[];
|
|
543
544
|
description?: string;
|
|
545
|
+
backgroundColor?: string;
|
|
544
546
|
}
|
|
545
547
|
|
|
546
548
|
interface SymbolDefinition {
|
|
@@ -681,6 +683,15 @@ interface UseViewBoxResult {
|
|
|
681
683
|
zoomIn: () => void;
|
|
682
684
|
/** Zoom out (increase viewBox size) */
|
|
683
685
|
zoomOut: () => void;
|
|
686
|
+
/** Fit viewBox to content bounds with optional padding */
|
|
687
|
+
fitToContent: (bounds: {
|
|
688
|
+
minX: number;
|
|
689
|
+
minY: number;
|
|
690
|
+
maxX: number;
|
|
691
|
+
maxY: number;
|
|
692
|
+
}, padding?: number) => void;
|
|
693
|
+
/** Set viewBox directly */
|
|
694
|
+
setViewBox: (viewBox: ViewBox) => void;
|
|
684
695
|
/** Convert screen coordinates to world (SVG) coordinates */
|
|
685
696
|
screenToWorld: (screenX: number, screenY: number) => {
|
|
686
697
|
x: number;
|
|
@@ -888,6 +899,10 @@ interface PIDCanvasFeatures {
|
|
|
888
899
|
allowSymbolDrop?: boolean;
|
|
889
900
|
/** Enable connection drawing mode to create pipes between nodes (default: false) */
|
|
890
901
|
connectionMode?: boolean;
|
|
902
|
+
/** Show background grid (default: true) */
|
|
903
|
+
showGrid?: boolean;
|
|
904
|
+
/** Background color for canvas (default: '#ffffff') */
|
|
905
|
+
backgroundColor?: string;
|
|
891
906
|
}
|
|
892
907
|
interface PIDCanvasProps extends react__default.HTMLAttributes<HTMLDivElement> {
|
|
893
908
|
/** The diagram to render */
|
|
@@ -921,9 +936,19 @@ interface PIDCanvasProps extends react__default.HTMLAttributes<HTMLDivElement> {
|
|
|
921
936
|
}) => void;
|
|
922
937
|
/** Callback when a pipe is created via connection mode */
|
|
923
938
|
onPipeCreated?: (sourceNodeId: string, targetNodeId: string, sourcePortId?: string, targetPortId?: string) => void;
|
|
939
|
+
/** Callback when canvas is mounted, provides control functions */
|
|
940
|
+
onMounted?: (controls: PIDCanvasControls) => void;
|
|
924
941
|
/** Telemetry data bindings for live values */
|
|
925
942
|
telemetry?: TelemetryMap;
|
|
926
943
|
}
|
|
944
|
+
interface PIDCanvasControls {
|
|
945
|
+
/** Fit view to show all diagram content */
|
|
946
|
+
fitToContent: (padding?: number) => void;
|
|
947
|
+
/** Set zoom level (1 = 100%, 2 = 200%, etc.) */
|
|
948
|
+
setZoom: (zoomLevel: number) => void;
|
|
949
|
+
/** Get current zoom level */
|
|
950
|
+
getZoom: () => number;
|
|
951
|
+
}
|
|
927
952
|
/**
|
|
928
953
|
* PIDCanvas - Interactive canvas for rendering P&ID diagrams
|
|
929
954
|
*
|
|
@@ -933,7 +958,7 @@ interface PIDCanvasProps extends react__default.HTMLAttributes<HTMLDivElement> {
|
|
|
933
958
|
*
|
|
934
959
|
* Future: Editing tools, symbol library UI
|
|
935
960
|
*/
|
|
936
|
-
declare function PIDCanvas({ diagram, className, style, features, onSelectionChange, onNodeClick, onPipeClick, onNodeMove, onDiagramChange, onDelete, onItemsCopied, onPaste, onSymbolDrop, onPipeCreated, telemetry, ...props }: PIDCanvasProps): react__default.ReactElement;
|
|
961
|
+
declare function PIDCanvas({ diagram, className, style, features, onSelectionChange, onNodeClick, onPipeClick, onNodeMove, onDiagramChange, onDelete, onItemsCopied, onPaste, onSymbolDrop, onPipeCreated, onMounted, telemetry, ...props }: PIDCanvasProps): react__default.ReactElement;
|
|
937
962
|
|
|
938
963
|
interface SymbolLibraryProps {
|
|
939
964
|
/** Optional className for styling */
|
package/dist/index.d.ts
CHANGED
|
@@ -447,6 +447,7 @@ interface NodeInstance {
|
|
|
447
447
|
x: number;
|
|
448
448
|
y: number;
|
|
449
449
|
};
|
|
450
|
+
labelFontSize?: number;
|
|
450
451
|
metadata?: Record<string, unknown>;
|
|
451
452
|
}
|
|
452
453
|
interface Transform {
|
|
@@ -541,6 +542,7 @@ interface DiagramMetadata {
|
|
|
541
542
|
modified?: string;
|
|
542
543
|
tags?: string[];
|
|
543
544
|
description?: string;
|
|
545
|
+
backgroundColor?: string;
|
|
544
546
|
}
|
|
545
547
|
|
|
546
548
|
interface SymbolDefinition {
|
|
@@ -681,6 +683,15 @@ interface UseViewBoxResult {
|
|
|
681
683
|
zoomIn: () => void;
|
|
682
684
|
/** Zoom out (increase viewBox size) */
|
|
683
685
|
zoomOut: () => void;
|
|
686
|
+
/** Fit viewBox to content bounds with optional padding */
|
|
687
|
+
fitToContent: (bounds: {
|
|
688
|
+
minX: number;
|
|
689
|
+
minY: number;
|
|
690
|
+
maxX: number;
|
|
691
|
+
maxY: number;
|
|
692
|
+
}, padding?: number) => void;
|
|
693
|
+
/** Set viewBox directly */
|
|
694
|
+
setViewBox: (viewBox: ViewBox) => void;
|
|
684
695
|
/** Convert screen coordinates to world (SVG) coordinates */
|
|
685
696
|
screenToWorld: (screenX: number, screenY: number) => {
|
|
686
697
|
x: number;
|
|
@@ -888,6 +899,10 @@ interface PIDCanvasFeatures {
|
|
|
888
899
|
allowSymbolDrop?: boolean;
|
|
889
900
|
/** Enable connection drawing mode to create pipes between nodes (default: false) */
|
|
890
901
|
connectionMode?: boolean;
|
|
902
|
+
/** Show background grid (default: true) */
|
|
903
|
+
showGrid?: boolean;
|
|
904
|
+
/** Background color for canvas (default: '#ffffff') */
|
|
905
|
+
backgroundColor?: string;
|
|
891
906
|
}
|
|
892
907
|
interface PIDCanvasProps extends react__default.HTMLAttributes<HTMLDivElement> {
|
|
893
908
|
/** The diagram to render */
|
|
@@ -921,9 +936,19 @@ interface PIDCanvasProps extends react__default.HTMLAttributes<HTMLDivElement> {
|
|
|
921
936
|
}) => void;
|
|
922
937
|
/** Callback when a pipe is created via connection mode */
|
|
923
938
|
onPipeCreated?: (sourceNodeId: string, targetNodeId: string, sourcePortId?: string, targetPortId?: string) => void;
|
|
939
|
+
/** Callback when canvas is mounted, provides control functions */
|
|
940
|
+
onMounted?: (controls: PIDCanvasControls) => void;
|
|
924
941
|
/** Telemetry data bindings for live values */
|
|
925
942
|
telemetry?: TelemetryMap;
|
|
926
943
|
}
|
|
944
|
+
interface PIDCanvasControls {
|
|
945
|
+
/** Fit view to show all diagram content */
|
|
946
|
+
fitToContent: (padding?: number) => void;
|
|
947
|
+
/** Set zoom level (1 = 100%, 2 = 200%, etc.) */
|
|
948
|
+
setZoom: (zoomLevel: number) => void;
|
|
949
|
+
/** Get current zoom level */
|
|
950
|
+
getZoom: () => number;
|
|
951
|
+
}
|
|
927
952
|
/**
|
|
928
953
|
* PIDCanvas - Interactive canvas for rendering P&ID diagrams
|
|
929
954
|
*
|
|
@@ -933,7 +958,7 @@ interface PIDCanvasProps extends react__default.HTMLAttributes<HTMLDivElement> {
|
|
|
933
958
|
*
|
|
934
959
|
* Future: Editing tools, symbol library UI
|
|
935
960
|
*/
|
|
936
|
-
declare function PIDCanvas({ diagram, className, style, features, onSelectionChange, onNodeClick, onPipeClick, onNodeMove, onDiagramChange, onDelete, onItemsCopied, onPaste, onSymbolDrop, onPipeCreated, telemetry, ...props }: PIDCanvasProps): react__default.ReactElement;
|
|
961
|
+
declare function PIDCanvas({ diagram, className, style, features, onSelectionChange, onNodeClick, onPipeClick, onNodeMove, onDiagramChange, onDelete, onItemsCopied, onPaste, onSymbolDrop, onPipeCreated, onMounted, telemetry, ...props }: PIDCanvasProps): react__default.ReactElement;
|
|
937
962
|
|
|
938
963
|
interface SymbolLibraryProps {
|
|
939
964
|
/** Optional className for styling */
|
package/dist/index.js
CHANGED
|
@@ -1964,9 +1964,10 @@ function NodeRenderer({
|
|
|
1964
1964
|
x: symbol.viewBox.width / 2 + (node.labelOffset?.x ?? 0),
|
|
1965
1965
|
y: symbol.viewBox.height + 12 + (node.labelOffset?.y ?? 0),
|
|
1966
1966
|
textAnchor: "middle",
|
|
1967
|
-
fontSize:
|
|
1967
|
+
fontSize: node.labelFontSize ?? 10,
|
|
1968
1968
|
fontWeight: "600",
|
|
1969
1969
|
fill: isSelected ? "#3b82f6" : "#1f2937",
|
|
1970
|
+
transform: node.transform.rotation ? `rotate(${-node.transform.rotation}, ${symbol.viewBox.width / 2 + (node.labelOffset?.x ?? 0)}, ${symbol.viewBox.height + 12 + (node.labelOffset?.y ?? 0)})` : void 0,
|
|
1970
1971
|
children: node.label || node.id
|
|
1971
1972
|
}
|
|
1972
1973
|
)
|
|
@@ -2346,6 +2347,34 @@ function useViewBox(options) {
|
|
|
2346
2347
|
};
|
|
2347
2348
|
});
|
|
2348
2349
|
}, [initialViewBox, maxZoom]);
|
|
2350
|
+
const fitToContent = useCallback3((bounds, padding = 50) => {
|
|
2351
|
+
const contentWidth = bounds.maxX - bounds.minX;
|
|
2352
|
+
const contentHeight = bounds.maxY - bounds.minY;
|
|
2353
|
+
if (contentWidth === 0 || contentHeight === 0) {
|
|
2354
|
+
return;
|
|
2355
|
+
}
|
|
2356
|
+
const paddedWidth = contentWidth + padding * 2;
|
|
2357
|
+
const paddedHeight = contentHeight + padding * 2;
|
|
2358
|
+
const aspectRatio = initialViewBox.width / initialViewBox.height;
|
|
2359
|
+
const contentAspectRatio = paddedWidth / paddedHeight;
|
|
2360
|
+
let newWidth;
|
|
2361
|
+
let newHeight;
|
|
2362
|
+
if (contentAspectRatio > aspectRatio) {
|
|
2363
|
+
newWidth = paddedWidth;
|
|
2364
|
+
newHeight = paddedWidth / aspectRatio;
|
|
2365
|
+
} else {
|
|
2366
|
+
newHeight = paddedHeight;
|
|
2367
|
+
newWidth = paddedHeight * aspectRatio;
|
|
2368
|
+
}
|
|
2369
|
+
const centerX = bounds.minX + contentWidth / 2;
|
|
2370
|
+
const centerY = bounds.minY + contentHeight / 2;
|
|
2371
|
+
setViewBox({
|
|
2372
|
+
x: centerX - newWidth / 2,
|
|
2373
|
+
y: centerY - newHeight / 2,
|
|
2374
|
+
width: newWidth,
|
|
2375
|
+
height: newHeight
|
|
2376
|
+
});
|
|
2377
|
+
}, [initialViewBox]);
|
|
2349
2378
|
useEffect4(() => {
|
|
2350
2379
|
if (!enableZoom || !svgRef.current) return;
|
|
2351
2380
|
const svg = svgRef.current;
|
|
@@ -2577,6 +2606,8 @@ function useViewBox(options) {
|
|
|
2577
2606
|
resetViewBox,
|
|
2578
2607
|
zoomIn,
|
|
2579
2608
|
zoomOut,
|
|
2609
|
+
fitToContent,
|
|
2610
|
+
setViewBox,
|
|
2580
2611
|
screenToWorld,
|
|
2581
2612
|
svgRef
|
|
2582
2613
|
};
|
|
@@ -3231,6 +3262,7 @@ function PIDCanvas({
|
|
|
3231
3262
|
onPaste,
|
|
3232
3263
|
onSymbolDrop,
|
|
3233
3264
|
onPipeCreated,
|
|
3265
|
+
onMounted,
|
|
3234
3266
|
telemetry,
|
|
3235
3267
|
...props
|
|
3236
3268
|
}) {
|
|
@@ -3248,12 +3280,72 @@ function PIDCanvas({
|
|
|
3248
3280
|
const {
|
|
3249
3281
|
viewBox: controlledViewBox,
|
|
3250
3282
|
svgRef,
|
|
3251
|
-
screenToWorld
|
|
3283
|
+
screenToWorld,
|
|
3284
|
+
fitToContent: fitToContentViewBox,
|
|
3285
|
+
setViewBox
|
|
3252
3286
|
} = useViewBox({
|
|
3253
3287
|
initialViewBox: diagram.viewBox,
|
|
3254
3288
|
enablePan: features.pan ?? true,
|
|
3255
3289
|
enableZoom: features.zoom ?? true
|
|
3256
3290
|
});
|
|
3291
|
+
const calculateContentBounds = useCallback7(() => {
|
|
3292
|
+
const nodes2 = localDiagram.nodes.filter((n) => n.visible !== false);
|
|
3293
|
+
if (nodes2.length === 0) {
|
|
3294
|
+
return null;
|
|
3295
|
+
}
|
|
3296
|
+
let minX = Infinity;
|
|
3297
|
+
let minY = Infinity;
|
|
3298
|
+
let maxX = -Infinity;
|
|
3299
|
+
let maxY = -Infinity;
|
|
3300
|
+
nodes2.forEach((node) => {
|
|
3301
|
+
const symbol = getSymbolDefinition(node.symbolId);
|
|
3302
|
+
if (!symbol) return;
|
|
3303
|
+
const nodeMinX = node.transform.x;
|
|
3304
|
+
const nodeMinY = node.transform.y;
|
|
3305
|
+
const nodeMaxX = node.transform.x + symbol.viewBox.width;
|
|
3306
|
+
const nodeMaxY = node.transform.y + symbol.viewBox.height;
|
|
3307
|
+
minX = Math.min(minX, nodeMinX);
|
|
3308
|
+
minY = Math.min(minY, nodeMinY);
|
|
3309
|
+
maxX = Math.max(maxX, nodeMaxX);
|
|
3310
|
+
maxY = Math.max(maxY, nodeMaxY);
|
|
3311
|
+
});
|
|
3312
|
+
return { minX, minY, maxX, maxY };
|
|
3313
|
+
}, [localDiagram.nodes]);
|
|
3314
|
+
useEffect8(() => {
|
|
3315
|
+
if (onMounted) {
|
|
3316
|
+
const controls = {
|
|
3317
|
+
fitToContent: (padding = 50) => {
|
|
3318
|
+
const bounds = calculateContentBounds();
|
|
3319
|
+
if (bounds) {
|
|
3320
|
+
fitToContentViewBox(bounds, padding);
|
|
3321
|
+
}
|
|
3322
|
+
},
|
|
3323
|
+
setZoom: (zoomLevel) => {
|
|
3324
|
+
const centerX = controlledViewBox.x + controlledViewBox.width / 2;
|
|
3325
|
+
const centerY = controlledViewBox.y + controlledViewBox.height / 2;
|
|
3326
|
+
const newWidth = diagram.viewBox.width / zoomLevel;
|
|
3327
|
+
const newHeight = diagram.viewBox.height / zoomLevel;
|
|
3328
|
+
setViewBox({
|
|
3329
|
+
x: centerX - newWidth / 2,
|
|
3330
|
+
y: centerY - newHeight / 2,
|
|
3331
|
+
width: newWidth,
|
|
3332
|
+
height: newHeight
|
|
3333
|
+
});
|
|
3334
|
+
},
|
|
3335
|
+
getZoom: () => {
|
|
3336
|
+
return diagram.viewBox.width / controlledViewBox.width;
|
|
3337
|
+
}
|
|
3338
|
+
};
|
|
3339
|
+
onMounted(controls);
|
|
3340
|
+
}
|
|
3341
|
+
}, [
|
|
3342
|
+
onMounted,
|
|
3343
|
+
calculateContentBounds,
|
|
3344
|
+
fitToContentViewBox,
|
|
3345
|
+
setViewBox,
|
|
3346
|
+
controlledViewBox,
|
|
3347
|
+
diagram.viewBox
|
|
3348
|
+
]);
|
|
3257
3349
|
const { selection, selectNode, selectPipe, clearSelection } = useSelection({
|
|
3258
3350
|
multiSelect: features.multiSelect ?? true,
|
|
3259
3351
|
onSelectionChange
|
|
@@ -3544,21 +3636,32 @@ function PIDCanvas({
|
|
|
3544
3636
|
onDrop: handleDrop,
|
|
3545
3637
|
onDragLeave: handleDragLeave,
|
|
3546
3638
|
children: [
|
|
3547
|
-
/* @__PURE__ */ jsxs12(
|
|
3548
|
-
/* @__PURE__ */
|
|
3549
|
-
|
|
3550
|
-
/* @__PURE__ */
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3639
|
+
features.showGrid !== false ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
3640
|
+
/* @__PURE__ */ jsxs12("defs", { children: [
|
|
3641
|
+
/* @__PURE__ */ jsx16("pattern", { id: "grid-minor", width: "5", height: "5", patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ jsx16("path", { d: "M 5 0 L 0 0 0 5", fill: "none", stroke: "#e5e7eb", strokeWidth: "0.5" }) }),
|
|
3642
|
+
/* @__PURE__ */ jsxs12("pattern", { id: "grid-major", width: "25", height: "25", patternUnits: "userSpaceOnUse", children: [
|
|
3643
|
+
/* @__PURE__ */ jsx16("rect", { width: "25", height: "25", fill: "url(#grid-minor)" }),
|
|
3644
|
+
/* @__PURE__ */ jsx16("path", { d: "M 25 0 L 0 0 0 25", fill: "none", stroke: "#d1d5db", strokeWidth: "1" })
|
|
3645
|
+
] })
|
|
3646
|
+
] }),
|
|
3647
|
+
/* @__PURE__ */ jsx16(
|
|
3648
|
+
"rect",
|
|
3649
|
+
{
|
|
3650
|
+
x: Math.floor(viewBox.x / 25) * 25 - viewBox.width,
|
|
3651
|
+
y: Math.floor(viewBox.y / 25) * 25 - viewBox.height,
|
|
3652
|
+
width: Math.ceil(viewBox.width * 3 / 25) * 25,
|
|
3653
|
+
height: Math.ceil(viewBox.height * 3 / 25) * 25,
|
|
3654
|
+
fill: "url(#grid-major)"
|
|
3655
|
+
}
|
|
3656
|
+
)
|
|
3657
|
+
] }) : /* @__PURE__ */ jsx16(
|
|
3555
3658
|
"rect",
|
|
3556
3659
|
{
|
|
3557
3660
|
x: Math.floor(viewBox.x / 25) * 25 - viewBox.width,
|
|
3558
3661
|
y: Math.floor(viewBox.y / 25) * 25 - viewBox.height,
|
|
3559
3662
|
width: Math.ceil(viewBox.width * 3 / 25) * 25,
|
|
3560
3663
|
height: Math.ceil(viewBox.height * 3 / 25) * 25,
|
|
3561
|
-
fill: "
|
|
3664
|
+
fill: features.backgroundColor ?? "#ffffff"
|
|
3562
3665
|
}
|
|
3563
3666
|
),
|
|
3564
3667
|
/* @__PURE__ */ jsx16(
|
|
@@ -3991,6 +4094,54 @@ function NodeConfigPanel({
|
|
|
3991
4094
|
}
|
|
3992
4095
|
)
|
|
3993
4096
|
] })
|
|
4097
|
+
] }),
|
|
4098
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
4099
|
+
/* @__PURE__ */ jsx18("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Font Size:" }),
|
|
4100
|
+
/* @__PURE__ */ jsx18(
|
|
4101
|
+
"input",
|
|
4102
|
+
{
|
|
4103
|
+
type: "number",
|
|
4104
|
+
min: "6",
|
|
4105
|
+
max: "24",
|
|
4106
|
+
value: node.labelFontSize ?? 10,
|
|
4107
|
+
onChange: (e) => onUpdateNode({
|
|
4108
|
+
labelFontSize: parseFloat(e.target.value) || 10
|
|
4109
|
+
}),
|
|
4110
|
+
style: {
|
|
4111
|
+
width: "100%",
|
|
4112
|
+
padding: "6px 8px",
|
|
4113
|
+
borderRadius: "4px",
|
|
4114
|
+
border: "1px solid #cbd5e1",
|
|
4115
|
+
fontSize: "0.875rem"
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
)
|
|
4119
|
+
] }),
|
|
4120
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
4121
|
+
/* @__PURE__ */ jsx18("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Rotation (degrees):" }),
|
|
4122
|
+
/* @__PURE__ */ jsx18(
|
|
4123
|
+
"input",
|
|
4124
|
+
{
|
|
4125
|
+
type: "number",
|
|
4126
|
+
min: "0",
|
|
4127
|
+
max: "360",
|
|
4128
|
+
step: "15",
|
|
4129
|
+
value: node.transform.rotation ?? 0,
|
|
4130
|
+
onChange: (e) => onUpdateNode({
|
|
4131
|
+
transform: {
|
|
4132
|
+
...node.transform,
|
|
4133
|
+
rotation: parseFloat(e.target.value) || 0
|
|
4134
|
+
}
|
|
4135
|
+
}),
|
|
4136
|
+
style: {
|
|
4137
|
+
width: "100%",
|
|
4138
|
+
padding: "6px 8px",
|
|
4139
|
+
borderRadius: "4px",
|
|
4140
|
+
border: "1px solid #cbd5e1",
|
|
4141
|
+
fontSize: "0.875rem"
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
)
|
|
3994
4145
|
] })
|
|
3995
4146
|
] }),
|
|
3996
4147
|
/* @__PURE__ */ jsxs14("div", { style: { marginBottom: "20px" }, children: [
|