fix(rack-planner): resolve infinite re-render loop in ConnectionLayer and add null-safety for VLAN tooltips
This commit is contained in:
@@ -40,11 +40,17 @@ export function ConnectionLayer() {
|
|||||||
// Also re-calculate if the user scrolls (though ideally lines are pinned to the canvas)
|
// Also re-calculate if the user scrolls (though ideally lines are pinned to the canvas)
|
||||||
// Actually, if SVG is INSIDE the scrollable container, we don't need scroll adjustment.
|
// Actually, if SVG is INSIDE the scrollable container, we don't need scroll adjustment.
|
||||||
|
|
||||||
// We'll use a MutationObserver to detect DOM changes (like modules being added/moved)
|
// Use a MutationObserver to detect DOM changes (like modules being added/moved)
|
||||||
const observer = new MutationObserver(updateCoords);
|
const observer = new MutationObserver(() => {
|
||||||
|
// Small debounce or check if it was our OWN SVG that changed
|
||||||
|
updateCoords();
|
||||||
|
});
|
||||||
|
|
||||||
const canvas = document.querySelector('.rack-planner-canvas');
|
const canvas = document.querySelector('.rack-planner-canvas');
|
||||||
if (canvas) {
|
if (canvas) {
|
||||||
observer.observe(canvas, { childList: true, subtree: true, attributes: true });
|
// DO NOT observe the entire subtree with attributes if it includes the ConnectionLayer
|
||||||
|
// Instead, just watch for module layout changes
|
||||||
|
observer.observe(canvas, { childList: true, subtree: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ export function ModuleBlock({ module }: ModuleBlockProps) {
|
|||||||
onClick={(e) => handlePortClick(e, port.id)}
|
onClick={(e) => handlePortClick(e, port.id)}
|
||||||
aria-label={`Port ${port.portNumber}`}
|
aria-label={`Port ${port.portNumber}`}
|
||||||
title={`Port ${port.portNumber}${port.label ? ` · ${port.label}` : ''}${
|
title={`Port ${port.portNumber}${port.label ? ` · ${port.label}` : ''}${
|
||||||
hasVlan ? ` (VLAN ${port.vlans.map((v) => v.vlan.vlanId).join(',')})` : ''
|
hasVlan ? ` (VLAN ${port.vlans.map((v) => v.vlan?.vlanId).filter(Boolean).join(',')})` : ''
|
||||||
}\nShift+Click for settings`}
|
}\nShift+Click for settings`}
|
||||||
style={{ backgroundColor: vlanColor, borderColor: 'rgba(0,0,0,0.2)' }}
|
style={{ backgroundColor: vlanColor, borderColor: 'rgba(0,0,0,0.2)' }}
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@@ -258,22 +258,24 @@ export function RackPlanner() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<SortableContext items={rackIds} strategy={horizontalListSortingStrategy}>
|
<>
|
||||||
<div
|
<SortableContext items={rackIds} strategy={horizontalListSortingStrategy}>
|
||||||
ref={canvasRef}
|
<div
|
||||||
className="flex gap-4 p-4 min-h-full items-start"
|
ref={canvasRef}
|
||||||
style={{ background: '#0f1117' }}
|
className="flex gap-4 p-4 min-h-full items-start"
|
||||||
>
|
style={{ background: '#0f1117' }}
|
||||||
{racks.map((rack) => (
|
>
|
||||||
<RackColumn
|
{racks.map((rack) => (
|
||||||
key={rack.id}
|
<RackColumn
|
||||||
rack={rack}
|
key={rack.id}
|
||||||
hoverSlot={hoverSlot}
|
rack={rack}
|
||||||
/>
|
hoverSlot={hoverSlot}
|
||||||
))}
|
/>
|
||||||
<ConnectionLayer />
|
))}
|
||||||
</div>
|
</div>
|
||||||
</SortableContext>
|
</SortableContext>
|
||||||
|
<ConnectionLayer />
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user