Skip to content
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>10.0.20</Version>
<Version>10.0.21</Version>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,26 @@ 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 || {}
// The group may have moved (floated/docked) since this panel was closed; getFloatingId toggles its grid<->floating
// id. If our entry is empty/gone but the counterpart holds the panels, route there so the panel rejoins the group.
if (!group || group.panels.length === 0) {
const counterpart = dockview.api.getGroup(getFloatingId(panel.groupId))
if (counterpart && counterpart.panels.length > 0) group = counterpart
}
// 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') {
Expand All @@ -73,15 +85,32 @@ 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,
inactive: true,
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) => {
Expand Down Expand Up @@ -175,6 +204,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');
}
Expand Down Expand Up @@ -204,6 +237,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)
Expand Down Expand Up @@ -621,6 +660,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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Loading