diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-extensions.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-extensions.js index b6d6dc5e..cc4f5539 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-extensions.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-extensions.js @@ -62,7 +62,12 @@ DockviewComponent.prototype.removeGroup = function (...args) { } else if (type == 'floating') { removeDrawerBtn(group) - return removeGroup.apply(this, args) + // Close panels (like the grid path) so each fires _panelVisibleChanged; the last close re-enters empty and removes the group. + const panels = [...group.panels] + if (panels.length === 0) { + return removeGroup.apply(this, args) + } + panels.forEach(panel => panel.api.close()) } } diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js index 175084c5..2b72e762 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js @@ -39,14 +39,20 @@ const addGroupWithPanel = (dockview, panel, panels, index) => { const addPanelWidthGroupId = (dockview, panel, index) => { let group = dockview.api.getGroup(panel.groupId) - let { rect = {}, packup, floatType, drawer, direction = 'left' } = panel.params || {} + // Empty pre-existing group = deleted-side placeholder (deferred actions + collapsed branch); a populated one is healthy. + const reusedEmptyGroup = !!group && group.panels.length === 0; + const isNewFloatingGroup = !group; + let { rect = {}, packup, floatType, drawer, direction = 'left', currentPosition } = panel.params || {} if (!group) { group = dockview.createGroup({ id: panel.groupId }) const width = dockview.width > 500 ? 500 : (dockview.width - 10) const height = dockview.height > 460 ? 460 : (dockview.height - 10) const left = (dockview.width - width) / 2 const top = (dockview.height - height) / 2 - let floatingGroupRect = rect || { + // Prefer currentPosition (saved on hide) over rect (only refreshed on un-float) to keep last size & position. + let floatingGroupRect = (currentPosition?.width > 0 + ? { width: currentPosition.width, height: currentPosition.height, position: { top: currentPosition.top, left: currentPosition.left } } + : rect) || { width, height: packup?.isPackup ? packup.height : height, position: { left, top } } if (floatType == 'drawer') { @@ -73,6 +79,10 @@ const addPanelWidthGroupId = (dockview, panel, index) => { } } + // Placeholder branch collapsed to width 0 in the saved layout; restore the pre-delete width (currentPosition) + // via initialWidth → setSize, else it re-shows at min ~100. + const restoreWidth = reusedEmptyGroup && group.api.location.type === 'grid' + ? panel.params?.currentPosition?.width : undefined; dockview.addPanel({ id: panel.id, title: panel.title, @@ -80,8 +90,21 @@ const addPanelWidthGroupId = (dockview, panel, index) => { renderer: panel.renderer, component: panel.component, position: { referenceGroup: group, index: index || 0 }, + initialWidth: restoreWidth > 0 ? restoreWidth : undefined, params: { ...panel.params, rect, packup, visible: true } }) + + // addPanel is inactive; activate so a freshly created floating group isn't blank. + if (isNewFloatingGroup) { + group.panels.find(p => p.id === panel.id)?.api.setActive(); + } + + // Placeholder deferred its action states while empty (see resetActionStates); re-render now it has a panel. + if (reusedEmptyGroup && group.api.location.type === 'grid') { + reRenderActionStates(group); + // initialWidth left a _pendingSize; clear it so a later setVisible(true) (float->dock) won't replay this stale width. + if (restoreWidth > 0) group.api._pendingSize = undefined; + } } const addPanelWidthCreatGroup = (dockview, panel, panels) => { @@ -175,6 +198,10 @@ const disposeGroup = group => { const resetActionStates = (group, actionContainer, groupType) => { const dockview = group.api.accessor; + // Empty group: `[].every()` is vacuously true so every show*() falls back to options defaults, wrongly showing + // buttons that stick once re-filled. Defer until it has panels (re-rendered on insert in addPanelWidthGroupId). + if (group.panels.length === 0) return; + // bb-show-lock only gates the button; apply the lock STATE regardless so a locked group stays locked when showLock=false. if (showLock(dockview, group)) { actionContainer.classList.add('bb-show-lock'); } @@ -204,6 +231,12 @@ const resetActionStates = (group, actionContainer, groupType) => { } } +// Re-render action buttons after an empty placeholder gains a panel (its actions were deferred while empty). +const reRenderActionStates = group => { + const actionContainer = group.header.element.querySelector('.dv-right-actions-container'); + if (actionContainer) resetActionStates(group, actionContainer); +} + const showLock = (dockview, group) => { const { options } = dockview.params; return group.panels.every(panel => panel.params.showLock === null) @@ -621,6 +654,8 @@ const dock = (group, floatType) => { from: { group: group }, to: { group: originGroup, position: 'center' } }) + // originGroup was an empty placeholder while floated; its deferred action buttons need re-rendering now it is filled. + reRenderActionStates(originGroup) saveConfig(dockview) } diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js index 68a4e3a5..cc3a755a 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js @@ -65,8 +65,9 @@ const onRemovePanel = event => { currentPosition: { width: event.group.element.parentElement.offsetWidth, height: event.group.element.parentElement.offsetHeight, - top: parseFloat(event.group.element.parentElement.style.top || 0), - left: parseFloat(event.group.element.parentElement.style.left || 0) + // offsetTop/Left, not style.top/left which can be 'auto' -> parseFloat NaN -> lost position. + top: event.group.element.parentElement.offsetTop, + left: event.group.element.parentElement.offsetLeft }, index: event.group.delPanelIndex }