@procaaso/alphinity-ui-components 1.8.1 → 1.8.2

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/dist/index.d.cts CHANGED
@@ -1481,9 +1481,22 @@ interface PIDCanvasControls {
1481
1481
  * - Renders nodes (PR #3) and pipes (PR #4) with static layout
1482
1482
  * - Drag-and-drop: Drag nodes to reposition (PR #8)
1483
1483
  *
1484
+ * Memoized (renderer-OOM fix): a host whose screen re-renders for reasons
1485
+ * unrelated to the canvas (panel state, connection status, ...) should not pay
1486
+ * a full-canvas reconciliation. The memo only pays off when the host passes
1487
+ * identity-stable props (memoized features/telemetry/style, useCallback
1488
+ * handlers); an unstable host degrades gracefully to the old behavior.
1489
+ *
1484
1490
  * Future: Editing tools, symbol library UI
1485
1491
  */
1486
- declare function PIDCanvas({ diagram, className, style, features, onSelectionChange, onNodeClick, onPipeClick, onNodeMove, onDiagramChange, onDelete, onItemsCopied, onPaste, onSymbolDrop, onPipeCreated, onMounted, telemetry, statusColorMap, ...props }: PIDCanvasProps): React__default.ReactElement;
1492
+ declare function PIDCanvasImpl({ diagram, className, style, features, onSelectionChange, onNodeClick, onPipeClick, onNodeMove, onDiagramChange, onDelete, onItemsCopied, onPaste, onSymbolDrop, onPipeCreated, onMounted, telemetry, statusColorMap, ...props }: PIDCanvasProps): React__default.ReactElement;
1493
+ /**
1494
+ * Memo boundary - see PIDCanvasImpl doc comment (renderer-OOM fix). Shallow
1495
+ * compare: `diagram`, `telemetry`, `features`, `style`, `statusColorMap`, and
1496
+ * every callback are compared by identity; hosts that keep them stable skip
1497
+ * the full-canvas re-render entirely when unrelated screen state changes.
1498
+ */
1499
+ declare const PIDCanvas: React__default.MemoExoticComponent<typeof PIDCanvasImpl>;
1487
1500
 
1488
1501
  interface SymbolLibraryProps {
1489
1502
  /** Optional className for styling */
package/dist/index.d.ts CHANGED
@@ -1481,9 +1481,22 @@ interface PIDCanvasControls {
1481
1481
  * - Renders nodes (PR #3) and pipes (PR #4) with static layout
1482
1482
  * - Drag-and-drop: Drag nodes to reposition (PR #8)
1483
1483
  *
1484
+ * Memoized (renderer-OOM fix): a host whose screen re-renders for reasons
1485
+ * unrelated to the canvas (panel state, connection status, ...) should not pay
1486
+ * a full-canvas reconciliation. The memo only pays off when the host passes
1487
+ * identity-stable props (memoized features/telemetry/style, useCallback
1488
+ * handlers); an unstable host degrades gracefully to the old behavior.
1489
+ *
1484
1490
  * Future: Editing tools, symbol library UI
1485
1491
  */
1486
- declare function PIDCanvas({ diagram, className, style, features, onSelectionChange, onNodeClick, onPipeClick, onNodeMove, onDiagramChange, onDelete, onItemsCopied, onPaste, onSymbolDrop, onPipeCreated, onMounted, telemetry, statusColorMap, ...props }: PIDCanvasProps): React__default.ReactElement;
1492
+ declare function PIDCanvasImpl({ diagram, className, style, features, onSelectionChange, onNodeClick, onPipeClick, onNodeMove, onDiagramChange, onDelete, onItemsCopied, onPaste, onSymbolDrop, onPipeCreated, onMounted, telemetry, statusColorMap, ...props }: PIDCanvasProps): React__default.ReactElement;
1493
+ /**
1494
+ * Memo boundary - see PIDCanvasImpl doc comment (renderer-OOM fix). Shallow
1495
+ * compare: `diagram`, `telemetry`, `features`, `style`, `statusColorMap`, and
1496
+ * every callback are compared by identity; hosts that keep them stable skip
1497
+ * the full-canvas re-render entirely when unrelated screen state changes.
1498
+ */
1499
+ declare const PIDCanvas: React__default.MemoExoticComponent<typeof PIDCanvasImpl>;
1487
1500
 
1488
1501
  interface SymbolLibraryProps {
1489
1502
  /** Optional className for styling */
package/dist/index.js CHANGED
@@ -3278,7 +3278,10 @@ var ThemeToggle = ({
3278
3278
  };
3279
3279
 
3280
3280
  // src/components/PIDCanvas/PIDCanvas.tsx
3281
- import { useCallback as useCallback8, useState as useState14, useEffect as useEffect12, useRef as useRef6 } from "react";
3281
+ import React13, { useCallback as useCallback8, useState as useState14, useEffect as useEffect12, useRef as useRef6 } from "react";
3282
+
3283
+ // src/components/PIDCanvas/NodeRenderer.tsx
3284
+ import React9 from "react";
3282
3285
 
3283
3286
  // src/diagram/symbols/mockSymbolLibrary.ts
3284
3287
  var mockSymbolLibrary = {
@@ -4500,7 +4503,7 @@ function getAvailableSymbols() {
4500
4503
 
4501
4504
  // src/components/PIDCanvas/NodeRenderer.tsx
4502
4505
  import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
4503
- function NodeRenderer({
4506
+ function NodeRendererImpl({
4504
4507
  nodes: nodes2,
4505
4508
  selectedNodeIds,
4506
4509
  onNodeClick,
@@ -4646,10 +4649,12 @@ function NodeRenderer({
4646
4649
  );
4647
4650
  }) });
4648
4651
  }
4652
+ var NodeRenderer = React9.memo(NodeRendererImpl);
4649
4653
 
4650
4654
  // src/components/PIDCanvas/PipeRenderer.tsx
4655
+ import React10 from "react";
4651
4656
  import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
4652
- function PipeRenderer({
4657
+ function PipeRendererImpl({
4653
4658
  pipes,
4654
4659
  selectedPipeIds,
4655
4660
  onPipeClick,
@@ -4826,8 +4831,10 @@ function PipeRenderer({
4826
4831
  );
4827
4832
  }) });
4828
4833
  }
4834
+ var PipeRenderer = React10.memo(PipeRendererImpl);
4829
4835
 
4830
4836
  // src/components/PIDCanvas/DataOverlay.tsx
4837
+ import React11 from "react";
4831
4838
  import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
4832
4839
  var PULSE_KEYFRAME_ID = "pid-data-overlay-pulse-keyframes";
4833
4840
  var PULSE_ANIMATION = "pid-data-overlay-pulse 1.5s ease-in-out infinite";
@@ -4853,7 +4860,7 @@ function renderStatusDot(cx, cy, scale, fill) {
4853
4860
  }
4854
4861
  );
4855
4862
  }
4856
- function DataOverlay({
4863
+ function DataOverlayImpl({
4857
4864
  nodeId,
4858
4865
  x,
4859
4866
  y,
@@ -4873,6 +4880,24 @@ function DataOverlay({
4873
4880
  const backgroundColor = display?.backgroundColor;
4874
4881
  const offsetX = display?.offsetX ?? 0;
4875
4882
  const offsetY = display?.offsetY ?? 0;
4883
+ const sparklinePath = React11.useMemo(() => {
4884
+ if (!showSparkline || !history || history.length < 2) return "";
4885
+ const width = 70 * scale;
4886
+ const height = 20 * scale;
4887
+ const padding = 5 * scale;
4888
+ const effectiveWidth = width - padding * 2;
4889
+ const effectiveHeight = height - padding * 2;
4890
+ const hasEuRange = scaling?.euLow !== void 0 && scaling?.euHigh !== void 0 && scaling.euHigh !== scaling.euLow;
4891
+ const min = hasEuRange ? scaling.euLow : Math.min(...history);
4892
+ const max = hasEuRange ? scaling.euHigh : Math.max(...history);
4893
+ const range = max - min || 1;
4894
+ const points = history.map((val, i) => {
4895
+ const xPos = i / (history.length - 1) * effectiveWidth + padding;
4896
+ const yPos = height - padding - (val - min) / range * effectiveHeight;
4897
+ return `${xPos.toFixed(2)},${yPos.toFixed(2)}`;
4898
+ });
4899
+ return `M ${points.join(" L ")}`;
4900
+ }, [showSparkline, history, scaling, scale]);
4876
4901
  if (!showValue) {
4877
4902
  return /* @__PURE__ */ jsx18(Fragment6, {});
4878
4903
  }
@@ -4892,24 +4917,6 @@ function DataOverlay({
4892
4917
  const finalTextColor = textColor || colors.text;
4893
4918
  const formattedValue = formatValue(value.value);
4894
4919
  const displayText = unit ? `${formattedValue} ${unit}` : formattedValue;
4895
- const sparklinePath = (() => {
4896
- if (!showSparkline || !history || history.length < 2) return "";
4897
- const width = 70 * scale;
4898
- const height = 20 * scale;
4899
- const padding = 5 * scale;
4900
- const effectiveWidth = width - padding * 2;
4901
- const effectiveHeight = height - padding * 2;
4902
- const hasEuRange = scaling?.euLow !== void 0 && scaling?.euHigh !== void 0 && scaling.euHigh !== scaling.euLow;
4903
- const min = hasEuRange ? scaling.euLow : Math.min(...history);
4904
- const max = hasEuRange ? scaling.euHigh : Math.max(...history);
4905
- const range = max - min || 1;
4906
- const points = history.map((val, i) => {
4907
- const xPos = i / (history.length - 1) * effectiveWidth + padding;
4908
- const yPos = height - padding - (val - min) / range * effectiveHeight;
4909
- return `${xPos.toFixed(2)},${yPos.toFixed(2)}`;
4910
- });
4911
- return `M ${points.join(" L ")}`;
4912
- })();
4913
4920
  const hasSparkline = showSparkline && sparklinePath;
4914
4921
  const overlayHeight = (label ? hasSparkline ? 55 : 30 : hasSparkline ? 47 : 22) * scale;
4915
4922
  const overlayWidth = 80 * scale;
@@ -4989,6 +4996,7 @@ function DataOverlay({
4989
4996
  ) })
4990
4997
  ] });
4991
4998
  }
4999
+ var DataOverlay = React11.memo(DataOverlayImpl);
4992
5000
 
4993
5001
  // src/components/PIDCanvas/PositionPanel.tsx
4994
5002
  import { useState as useState9, useEffect as useEffect6 } from "react";
@@ -6117,18 +6125,19 @@ function nodeHasPort(node, portId) {
6117
6125
 
6118
6126
  // src/components/PIDCanvas/PIDCanvas.tsx
6119
6127
  import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
6120
- function PIDCanvas({
6128
+ var DEFAULT_FEATURES = {
6129
+ pan: true,
6130
+ zoom: true,
6131
+ selection: false,
6132
+ multiSelect: true,
6133
+ telemetry: false,
6134
+ dragNodes: false
6135
+ };
6136
+ function PIDCanvasImpl({
6121
6137
  diagram,
6122
6138
  className = "",
6123
6139
  style,
6124
- features = {
6125
- pan: true,
6126
- zoom: true,
6127
- selection: false,
6128
- multiSelect: true,
6129
- telemetry: false,
6130
- dragNodes: false
6131
- },
6140
+ features = DEFAULT_FEATURES,
6132
6141
  onSelectionChange,
6133
6142
  onNodeClick,
6134
6143
  onPipeClick,
@@ -6589,52 +6598,71 @@ function PIDCanvas({
6589
6598
  if (!selectionEnabled) return;
6590
6599
  onPaste?.();
6591
6600
  }, [selectionEnabled, onPaste]);
6592
- const handleNodeDoubleClick = (nodeId, event) => {
6593
- if (!selectionEnabled) return;
6594
- console.log("Node double-clicked:", nodeId);
6595
- selectNode(nodeId, event.ctrlKey || event.metaKey);
6596
- };
6601
+ const handleNodeDoubleClick = useCallback8(
6602
+ (nodeId, event) => {
6603
+ if (!selectionEnabled) return;
6604
+ console.log("Node double-clicked:", nodeId);
6605
+ selectNode(nodeId, event.ctrlKey || event.metaKey);
6606
+ },
6607
+ [selectionEnabled, selectNode]
6608
+ );
6597
6609
  useKeyboardShortcuts({
6598
6610
  enabled: features.keyboardShortcuts ?? false,
6599
6611
  onDelete: handleDelete,
6600
6612
  onCopy: handleCopy,
6601
6613
  onPaste: handlePaste
6602
6614
  });
6603
- const handleNodeClick = (nodeId, event) => {
6604
- const connectionModeEnabled = features.connectionMode ?? false;
6605
- if (connectionModeEnabled && isConnecting) {
6606
- if (connectionSource && connectionSource !== nodeId) {
6607
- onPipeCreated?.(
6608
- connectionSource,
6609
- nodeId,
6610
- connectionSourcePort || void 0,
6611
- hoveredPort || void 0
6612
- );
6613
- setIsConnecting(false);
6614
- setConnectionSource(null);
6615
- setConnectionSourcePort(null);
6616
- setConnectionCursor(null);
6617
- setHoveredPort(null);
6615
+ const handleNodeClick = useCallback8(
6616
+ (nodeId, event) => {
6617
+ const connectionModeEnabled = features.connectionMode ?? false;
6618
+ if (connectionModeEnabled && isConnecting) {
6619
+ if (connectionSource && connectionSource !== nodeId) {
6620
+ onPipeCreated?.(
6621
+ connectionSource,
6622
+ nodeId,
6623
+ connectionSourcePort || void 0,
6624
+ hoveredPort || void 0
6625
+ );
6626
+ setIsConnecting(false);
6627
+ setConnectionSource(null);
6628
+ setConnectionSourcePort(null);
6629
+ setConnectionCursor(null);
6630
+ setHoveredPort(null);
6631
+ }
6632
+ return;
6618
6633
  }
6619
- return;
6620
- }
6621
- if (connectionModeEnabled && !isConnecting) {
6622
- setIsConnecting(true);
6623
- setConnectionSource(nodeId);
6624
- setConnectionSourcePort(hoveredPort);
6625
- return;
6626
- }
6627
- if (selectionEnabled) {
6628
- selectNode(nodeId, event.ctrlKey || event.metaKey);
6629
- }
6630
- onNodeClick?.(nodeId);
6631
- };
6632
- const handlePipeClick = (pipeId, event) => {
6633
- if (selectionEnabled) {
6634
- selectPipe(pipeId, event.ctrlKey || event.metaKey);
6635
- }
6636
- onPipeClick?.(pipeId);
6637
- };
6634
+ if (connectionModeEnabled && !isConnecting) {
6635
+ setIsConnecting(true);
6636
+ setConnectionSource(nodeId);
6637
+ setConnectionSourcePort(hoveredPort);
6638
+ return;
6639
+ }
6640
+ if (selectionEnabled) {
6641
+ selectNode(nodeId, event.ctrlKey || event.metaKey);
6642
+ }
6643
+ onNodeClick?.(nodeId);
6644
+ },
6645
+ [
6646
+ features.connectionMode,
6647
+ isConnecting,
6648
+ connectionSource,
6649
+ connectionSourcePort,
6650
+ hoveredPort,
6651
+ onPipeCreated,
6652
+ selectionEnabled,
6653
+ selectNode,
6654
+ onNodeClick
6655
+ ]
6656
+ );
6657
+ const handlePipeClick = useCallback8(
6658
+ (pipeId, event) => {
6659
+ if (selectionEnabled) {
6660
+ selectPipe(pipeId, event.ctrlKey || event.metaKey);
6661
+ }
6662
+ onPipeClick?.(pipeId);
6663
+ },
6664
+ [selectionEnabled, selectPipe, onPipeClick]
6665
+ );
6638
6666
  const handleCanvasClick = (e) => {
6639
6667
  const target = e.target;
6640
6668
  if (target.closest(".pipe-route-point")) {
@@ -6982,6 +7010,7 @@ function PIDCanvas({
6982
7010
  }
6983
7011
  );
6984
7012
  }
7013
+ var PIDCanvas = React13.memo(PIDCanvasImpl);
6985
7014
 
6986
7015
  // src/components/SymbolLibrary/SymbolLibrary.tsx
6987
7016
  import { useState as useState15 } from "react";