From d8916778c8573a7d2ebae487f42756953bccfe1e Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Fri, 18 Jul 2025 21:46:52 +0100 Subject: [PATCH] fix: prevent ghost group creation when dragging popout groups back to grid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves issue #960 where dragging a popout group back to the main grid would create an additional empty "ghost" group at the leftmost position. Changes: - Unified popout disposal logic to prevent automatic restoration - Always remove popout groups from tracking array before disposal - Clean up hidden reference groups that could become ghosts - Use manual window disposal instead of disposable.dispose() - Added proper support for popout-to-floating group moves - Fixed conditional logic for grid placement 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../src/dockview/dockviewComponent.ts | 104 ++++++++++++------ 1 file changed, 69 insertions(+), 35 deletions(-) diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index ad69c25a1..a6756a899 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -2368,50 +2368,48 @@ export class DockviewComponent throw new Error('failed to find popout group'); } - // For cross-window moves, we need to prevent the automatic restoration - // to the reference group that happens in the disposal cleanup - const isMovingToNewTarget = to.api.location.type === 'grid'; + // Remove from popout groups list to prevent automatic restoration + const index = this._popoutGroups.indexOf(selectedPopoutGroup); + if (index >= 0) { + this._popoutGroups.splice(index, 1); + } - if (isMovingToNewTarget) { - // For cross-window moves to new positions, we need to clean up properly - // Remove from popout groups list to prevent automatic restoration - const index = this._popoutGroups.indexOf(selectedPopoutGroup); - if (index >= 0) { - this._popoutGroups.splice(index, 1); + // Clean up the reference group (ghost) if it exists and is hidden + if (selectedPopoutGroup.referenceGroup) { + const referenceGroup = this.getPanel(selectedPopoutGroup.referenceGroup); + if (referenceGroup && !referenceGroup.api.isVisible) { + this.doRemoveGroup(referenceGroup, { skipActive: true }); } - - // Clean up the reference group (ghost) if it exists and is hidden - if (selectedPopoutGroup.referenceGroup) { - const referenceGroup = this.getPanel(selectedPopoutGroup.referenceGroup); - if (referenceGroup && !referenceGroup.api.isVisible) { - this.doRemoveGroup(referenceGroup, { skipActive: true }); - } - } - - // Manually dispose the window without triggering restoration - selectedPopoutGroup.window.dispose(); - - // Update group's location and containers for main window + } + + // Manually dispose the window without triggering restoration + selectedPopoutGroup.window.dispose(); + + // Update group's location and containers for target + if (to.api.location.type === 'grid') { from.model.renderContainer = this.overlayRenderContainer; from.model.dropTargetContainer = this.rootDropTargetContainer; from.model.location = { type: 'grid' }; - } else { - // Normal disposal that will restore to reference group - selectedPopoutGroup.disposable.dispose(); + } else if (to.api.location.type === 'floating') { + from.model.renderContainer = this.overlayRenderContainer; + from.model.dropTargetContainer = this.rootDropTargetContainer; + from.model.location = { type: 'floating' }; } + + break; } } - // For moves to grid locations (including cross-window moves from popout) - const referenceLocation = getGridLocation(to.element); - const dropLocation = getRelativeLocation( - this.gridview.orientation, - referenceLocation, - target - ); - - // Add to grid for moves from non-popout sources, or for cross-window moves from popout - if (from.api.location.type !== 'popout' || to.api.location.type === 'grid') { + // For moves to grid locations + if (to.api.location.type === 'grid') { + const referenceLocation = getGridLocation(to.element); + const dropLocation = getRelativeLocation( + this.gridview.orientation, + referenceLocation, + target + ); + + // Add to grid for all moves targeting grid location let size: number; @@ -2431,6 +2429,42 @@ export class DockviewComponent } this.gridview.addView(from, size, dropLocation); + } else if (to.api.location.type === 'floating') { + // For moves to floating locations, add as floating group + // Get the position/size from the target floating group + const targetFloatingGroup = this._floatingGroups.find( + (x) => x.group === to + ); + if (targetFloatingGroup) { + const box = targetFloatingGroup.overlay.toJSON(); + + // Calculate position based on available properties + let left: number, top: number; + if ('left' in box) { + left = box.left + 50; + } else if ('right' in box) { + left = Math.max(0, box.right - box.width - 50); + } else { + left = 50; // Default fallback + } + + if ('top' in box) { + top = box.top + 50; + } else if ('bottom' in box) { + top = Math.max(0, box.bottom - box.height - 50); + } else { + top = 50; // Default fallback + } + + this.addFloatingGroup(from, { + height: box.height, + width: box.width, + position: { + left, + top, + }, + }); + } } }