From 063b323e1ed6f3b666f86e6fd0d98ed2dac05fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20Monda?= Date: Wed, 14 Oct 2026 14:54:13 +0200 Subject: [PATCH] fix: keep macro action order in sync after drag and drop Moving the list items changed the DOM order behind Angular's *ngFor view order, so the reordered model was rendered against a desynced view order, duplicating or misplacing macro actions (issue #3065). Restore the pre-drag DOM order in DragAndDropDirective before emitting the reordered model so Angular re-renders it consistently, and drop the obsolete isMacroReordering content-based trackBy hack that made Angular move views against the stale DOM order. --- .../macro/list/macro-list.component.html | 2 +- .../macro/list/macro-list.component.ts | 13 +---------- .../drag-and-drop/drag-and-drop.directive.ts | 22 +++++++++++++++++++ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/uhk-web/src/app/components/macro/list/macro-list.component.html b/packages/uhk-web/src/app/components/macro/list/macro-list.component.html index 8cff2e4f7d1..e549788d319 100644 --- a/packages/uhk-web/src/app/components/macro/list/macro-list.component.html +++ b/packages/uhk-web/src/app/components/macro/list/macro-list.component.html @@ -5,7 +5,7 @@ [dragAndDrop]="macro.macroActions" [dragAndDropHandle]="dragAndDropHandle" (dragAndDropChange)="macroActionReordered($event)"> - >(ElementRef); @@ -68,6 +69,7 @@ export class DragAndDropDirective implements OnDestroy { this.offsetX = event.clientX - rect.left; this.offsetY = event.clientY - rect.top; this.initialRect = rect; + this.initialOrder = Array.from(this.elementRef.nativeElement.children) as HTMLElement[]; window.addEventListener('pointermove', this.onPointerMove); window.addEventListener('pointerup', this.onPointerUp); @@ -110,6 +112,7 @@ export class DragAndDropDirective implements OnDestroy { } const reordered = this.dragging ? this.getReorderedModel() : undefined; + this.restoreDomOrder(); this.cleanup(); if (reordered) { @@ -117,6 +120,24 @@ export class DragAndDropDirective implements OnDestroy { } }; + /** + * Puts the list items back into their pre-drag DOM order. + * + * The reordering is shown live by moving the actual DOM nodes, but that leaves the DOM out of + * sync with the view order Angular tracks for the bound model. Restoring the original order + * before emitting lets Angular re-render the reordered model with its own, consistent view + * order instead of duplicating or misplacing items. + */ + private restoreDomOrder(): void { + const container = this.elementRef.nativeElement; + + for (const child of this.initialOrder) { + container.appendChild(child); + } + + this.initialOrder = []; + } + private beginDrag(): void { this.dragging = true; this.item.classList.add(TRANSIT_CLASS); @@ -309,5 +330,6 @@ export class DragAndDropDirective implements OnDestroy { this.mirror = undefined; this.pointerId = -1; this.initialRect = undefined; + this.initialOrder = []; } } \ No newline at end of file