From 851d7fc29c9f3028738561fee88a5853c9e8e91d Mon Sep 17 00:00:00 2001 From: Alina Date: Wed, 18 Feb 2026 11:36:25 +0300 Subject: [PATCH] fix: reposition cards inside devices after dagre auto-layout X6 setPosition() does not translate children, so after dagre moves devices, cards stay at old absolute positions. Add fixDeviceChildren() that ensures device height fits visible cards and repositions each card at the correct offset within its parent device. Co-Authored-By: Claude Opus 4.6 --- .../features/schema/layout/dagre-layout.ts | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/frontend/src/app/features/schema/layout/dagre-layout.ts b/frontend/src/app/features/schema/layout/dagre-layout.ts index 91cbb5b..d834ba9 100644 --- a/frontend/src/app/features/schema/layout/dagre-layout.ts +++ b/frontend/src/app/features/schema/layout/dagre-layout.ts @@ -4,6 +4,9 @@ import { LAYER_MAPPING } from '../../../constants/layer-mapping'; import { SITE_PADDING, SITE_HEADER_HEIGHT, + DEVICE_HEADER_HEIGHT, + DEVICE_MIN_HEIGHT, + CARD_HEIGHT, } from '../../../constants/sizes'; function getLayerForCategory(category: string): number { @@ -137,6 +140,45 @@ function layoutSiteDevices( return { minX, minY, maxX, maxY }; } +/** + * Fix device children after dagre layout: + * 1. Ensure device height accommodates all visible cards + * 2. Reposition card-nodes at correct absolute positions inside devices + * + * setPosition() in X6 does NOT translate children, so cards stay + * at their old positions when a device moves — this function corrects that. + */ +function fixDeviceChildren(graph: Graph): void { + for (const device of graph.getNodes()) { + if (device.shape === 'site-node' || device.shape === 'card-node') continue; + + const cards = (device.getChildren() ?? []).filter( + (c): c is Node => c.isNode() && c.shape === 'card-node' && c.isVisible(), + ); + if (cards.length === 0) continue; + + const data = device.getData<{ collapsed?: boolean }>(); + if (data?.collapsed) continue; + + // Ensure device is tall enough for its visible cards + const size = device.getSize(); + const cardsHeight = cards.length * (CARD_HEIGHT + 4) + 4; + const minHeight = Math.max(DEVICE_MIN_HEIGHT, DEVICE_HEADER_HEIGHT + cardsHeight + 6); + if (size.height < minHeight) { + device.resize(size.width, minHeight); + } + + // Place each card at the correct absolute position + const pos = device.getPosition(); + for (let i = 0; i < cards.length; i++) { + cards[i].setPosition( + pos.x + 10, + pos.y + DEVICE_HEADER_HEIGHT + 4 + i * (CARD_HEIGHT + 4), + ); + } + } +} + function getDeviceChildren(siteNode: Node): Node[] { return (siteNode.getChildren() ?? []).filter( (c): c is Node => @@ -264,5 +306,7 @@ export function applyDagreLayout(graph: Graph): void { cursorY = rootSiteY + rootSiteH + siteGap; } + fixDeviceChildren(graph); + graph.stopBatch('dagre-layout'); }