dockview-core
Version:
Zero dependency layout manager supporting tabs, groups, grids and splitviews for vanilla TypeScript
1,138 lines • 64.7 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DockviewGroupPanelModel = exports.DockviewWillDropEvent = exports.DockviewDidDropEvent = void 0;
var component_api_1 = require("../api/component.api");
var dataTransfer_1 = require("../dnd/dataTransfer");
var dom_1 = require("../dom");
var events_1 = require("../events");
var events_2 = require("./events");
var lifecycle_1 = require("../lifecycle");
var content_1 = require("./components/panel/content");
var tabsContainer_1 = require("./components/titlebar/tabsContainer");
var options_1 = require("./options");
var tabGroup_1 = require("./tabGroup");
var DockviewDidDropEvent = /** @class */ (function (_super) {
__extends(DockviewDidDropEvent, _super);
function DockviewDidDropEvent(options) {
var _this = _super.call(this) || this;
_this.options = options;
return _this;
}
Object.defineProperty(DockviewDidDropEvent.prototype, "nativeEvent", {
/**
* `PointerEvent` for touch drags has no `dataTransfer`; use
* `getData()` for the dockview payload regardless of input method.
*/
get: function () {
return this.options.nativeEvent;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewDidDropEvent.prototype, "position", {
get: function () {
return this.options.position;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewDidDropEvent.prototype, "panel", {
get: function () {
return this.options.panel;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewDidDropEvent.prototype, "group", {
get: function () {
return this.options.group;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewDidDropEvent.prototype, "api", {
get: function () {
return this.options.api;
},
enumerable: false,
configurable: true
});
DockviewDidDropEvent.prototype.getData = function () {
return this.options.getData();
};
return DockviewDidDropEvent;
}(events_1.DockviewEvent));
exports.DockviewDidDropEvent = DockviewDidDropEvent;
var DockviewWillDropEvent = /** @class */ (function (_super) {
__extends(DockviewWillDropEvent, _super);
function DockviewWillDropEvent(options) {
var _this = _super.call(this, options) || this;
_this._kind = options.kind;
return _this;
}
Object.defineProperty(DockviewWillDropEvent.prototype, "kind", {
get: function () {
return this._kind;
},
enumerable: false,
configurable: true
});
return DockviewWillDropEvent;
}(DockviewDidDropEvent));
exports.DockviewWillDropEvent = DockviewWillDropEvent;
var DockviewGroupPanelModel = /** @class */ (function (_super) {
__extends(DockviewGroupPanelModel, _super);
function DockviewGroupPanelModel(container, accessor, id, options, groupPanel) {
var _a, _b;
var _this = _super.call(this) || this;
_this.container = container;
_this.accessor = accessor;
_this.id = id;
_this.options = options;
_this.groupPanel = groupPanel;
_this._isGroupActive = false;
_this._locked = false;
_this._rightHeaderActionsDisposable = new lifecycle_1.MutableDisposable();
_this._leftHeaderActionsDisposable = new lifecycle_1.MutableDisposable();
_this._prefixHeaderActionsDisposable = new lifecycle_1.MutableDisposable();
_this._location = { type: 'grid' };
_this.mostRecentlyUsed = [];
_this._overwriteRenderContainer = null;
_this._overwriteDropTargetContainer = null;
_this._onDidChange = new events_1.Emitter();
_this.onDidChange = _this._onDidChange.event;
_this._width = 0;
_this._height = 0;
_this._panels = [];
_this._panelDisposables = new Map();
_this._tabGroupDisposables = new Map();
_this._pendingMicrotaskDisposables = new Set();
_this._onMove = new events_1.Emitter();
_this.onMove = _this._onMove.event;
_this._onDidDrop = new events_1.Emitter();
_this.onDidDrop = _this._onDidDrop.event;
_this._onWillDrop = new events_1.Emitter();
_this.onWillDrop = _this._onWillDrop.event;
_this._onWillShowOverlay = new events_1.Emitter();
_this.onWillShowOverlay = _this._onWillShowOverlay.event;
_this._onTabDragStart = new events_1.Emitter();
_this.onTabDragStart = _this._onTabDragStart.event;
_this._onGroupDragStart = new events_1.Emitter();
_this.onGroupDragStart = _this._onGroupDragStart.event;
_this._onDidAddPanel = new events_1.Emitter();
_this.onDidAddPanel = _this._onDidAddPanel.event;
_this._onDidPanelTitleChange = new events_1.Emitter();
_this.onDidPanelTitleChange = _this._onDidPanelTitleChange.event;
_this._onDidPanelParametersChange = new events_1.Emitter();
_this.onDidPanelParametersChange = _this._onDidPanelParametersChange.event;
_this._onDidRemovePanel = new events_1.Emitter();
_this.onDidRemovePanel = _this._onDidRemovePanel.event;
_this._onDidActivePanelChange = new events_1.Emitter();
_this.onDidActivePanelChange = _this._onDidActivePanelChange.event;
_this._onUnhandledDragOverEvent = new events_1.Emitter();
_this.onUnhandledDragOverEvent = _this._onUnhandledDragOverEvent.event;
_this._tabGroups = [];
_this._tabGroupMap = new Map();
_this._panelToTabGroup = new Map();
_this._tabGroupIdCounter = 0;
_this._pendingTabGroupUpdate = false;
_this._onDidCreateTabGroup = new events_1.Emitter();
_this.onDidCreateTabGroup = _this._onDidCreateTabGroup.event;
_this._onDidDestroyTabGroup = new events_1.Emitter();
_this.onDidDestroyTabGroup = _this._onDidDestroyTabGroup.event;
_this._onDidAddPanelToTabGroup = new events_1.Emitter();
_this.onDidAddPanelToTabGroup = _this._onDidAddPanelToTabGroup.event;
_this._onDidRemovePanelFromTabGroup = new events_1.Emitter();
_this.onDidRemovePanelFromTabGroup = _this._onDidRemovePanelFromTabGroup.event;
_this._onDidTabGroupChange = new events_1.Emitter();
_this.onDidTabGroupChange = _this._onDidTabGroupChange.event;
_this._onDidTabGroupCollapsedChange = new events_1.Emitter();
_this.onDidTabGroupCollapsedChange = _this._onDidTabGroupCollapsedChange.event;
(0, dom_1.toggleClass)(_this.container, 'dv-groupview', true);
_this._api = new component_api_1.DockviewApi(_this.accessor);
_this.tabsContainer = new tabsContainer_1.TabsContainer(_this.accessor, _this.groupPanel);
_this.contentContainer = new content_1.ContentContainer(_this.accessor, _this);
container.append(_this.tabsContainer.element, _this.contentContainer.element);
_this.header.hidden = !!options.hideHeader;
_this.locked = (_a = options.locked) !== null && _a !== void 0 ? _a : false;
_this.headerPosition =
(_b = options.headerPosition) !== null && _b !== void 0 ? _b : accessor.defaultHeaderPosition;
_this.addDisposables(_this._onTabDragStart, _this._onGroupDragStart, _this._onWillShowOverlay, _this._rightHeaderActionsDisposable, _this._leftHeaderActionsDisposable, _this._prefixHeaderActionsDisposable, _this.tabsContainer.onTabDragStart(function (event) {
_this._onTabDragStart.fire(event);
}), _this.tabsContainer.onGroupDragStart(function (event) {
_this._onGroupDragStart.fire(event);
}), _this.tabsContainer.onDrop(function (event) {
var e_1, _a;
var _b;
// Capture panel data before handleDropEvent (which may trigger moves)
var dragData = (0, dataTransfer_1.getPanelData)();
var draggedPanelId = (_b = dragData === null || dragData === void 0 ? void 0 : dragData.panelId) !== null && _b !== void 0 ? _b : null;
_this.handleDropEvent('header', event.event, 'center', event.index);
// Update tab group membership after the move completes
if (draggedPanelId && event.targetTabGroupId) {
// Compute the local index within the target tab group
// from the global panel index so the panel is inserted
// at the correct position, not just appended.
var tabGroup = _this._tabGroupMap.get(event.targetTabGroupId);
var localIndex = void 0;
if (tabGroup) {
var globalIdx = _this._panels.findIndex(function (p) { return p.id === draggedPanelId; });
if (globalIdx !== -1) {
// Count how many of this group's panels
// appear before the dragged panel
localIndex = 0;
var _loop_1 = function (pid) {
var pidIdx = _this._panels.findIndex(function (p) { return p.id === pid; });
if (pidIdx < globalIdx) {
localIndex++;
}
};
try {
for (var _c = __values(tabGroup.panelIds), _d = _c.next(); !_d.done; _d = _c.next()) {
var pid = _d.value;
_loop_1(pid);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
}
finally { if (e_1) throw e_1.error; }
}
}
}
_this.addPanelToTabGroup(event.targetTabGroupId, draggedPanelId, localIndex);
}
else if (draggedPanelId && event.targetTabGroupId === null) {
// Dropped outside any group — remove from current group
_this.removePanelFromTabGroup(draggedPanelId);
}
}), _this.contentContainer.onDidFocus(function () {
_this.accessor.doSetGroupActive(_this.groupPanel);
}), _this.contentContainer.onDidBlur(function () {
// noop
}), _this.contentContainer.dropTarget.onDrop(function (event) {
_this.handleDropEvent('content', event.nativeEvent, event.position);
}), _this.contentContainer.pointerDropTarget.onDrop(function (event) {
_this.handleDropEvent('content', event.nativeEvent, event.position);
}), _this.tabsContainer.onWillShowOverlay(function (event) {
_this._onWillShowOverlay.fire(event);
}), _this.contentContainer.dropTarget.onWillShowOverlay(function (event) {
_this._onWillShowOverlay.fire(new events_2.DockviewWillShowOverlayLocationEvent(event, {
kind: 'content',
panel: _this.activePanel,
api: _this._api,
group: _this.groupPanel,
getData: dataTransfer_1.getPanelData,
}));
}), _this.contentContainer.pointerDropTarget.onWillShowOverlay(function (event) {
_this._onWillShowOverlay.fire(new events_2.DockviewWillShowOverlayLocationEvent(event, {
kind: 'content',
panel: _this.activePanel,
api: _this._api,
group: _this.groupPanel,
getData: dataTransfer_1.getPanelData,
}));
}), _this._onMove, _this._onDidChange, _this._onDidDrop, _this._onWillDrop, _this._onDidAddPanel, _this._onDidRemovePanel, _this._onDidActivePanelChange, _this._onUnhandledDragOverEvent, _this._onDidPanelTitleChange, _this._onDidPanelParametersChange, _this._onDidCreateTabGroup, _this._onDidDestroyTabGroup, _this._onDidAddPanelToTabGroup, _this._onDidRemovePanelFromTabGroup, _this._onDidTabGroupChange, _this._onDidTabGroupCollapsedChange, _this._onDidCreateTabGroup.event(function () {
_this._scheduleTabGroupUpdate();
}), _this._onDidDestroyTabGroup.event(function () {
_this._scheduleTabGroupUpdate();
}), _this._onDidAddPanelToTabGroup.event(function () {
_this._scheduleTabGroupUpdate();
}), _this._onDidRemovePanelFromTabGroup.event(function () {
_this._scheduleTabGroupUpdate();
}), _this._onDidTabGroupChange.event(function () {
_this._scheduleTabGroupUpdate();
}), _this._onDidTabGroupCollapsedChange.event(function () {
_this._scheduleTabGroupUpdate();
}));
return _this;
}
Object.defineProperty(DockviewGroupPanelModel.prototype, "tabGroups", {
get: function () {
return this._tabGroups;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewGroupPanelModel.prototype, "element", {
get: function () {
throw new Error('dockview: not supported');
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewGroupPanelModel.prototype, "activePanel", {
get: function () {
return this._activePanel;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewGroupPanelModel.prototype, "locked", {
get: function () {
return this._locked;
},
set: function (value) {
this._locked = value;
(0, dom_1.toggleClass)(this.container, 'dv-locked-groupview', value === 'no-drop-target' || value);
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewGroupPanelModel.prototype, "isActive", {
get: function () {
return this._isGroupActive;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewGroupPanelModel.prototype, "panels", {
get: function () {
return this._panels;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewGroupPanelModel.prototype, "size", {
get: function () {
return this._panels.length;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewGroupPanelModel.prototype, "isEmpty", {
get: function () {
return this._panels.length === 0;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewGroupPanelModel.prototype, "hasWatermark", {
get: function () {
return !!(this.watermark && this.container.contains(this.watermark.element));
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewGroupPanelModel.prototype, "header", {
get: function () {
return this.tabsContainer;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewGroupPanelModel.prototype, "isContentFocused", {
get: function () {
if (!document.activeElement) {
return false;
}
return (0, dom_1.isAncestor)(document.activeElement, this.contentContainer.element);
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewGroupPanelModel.prototype, "headerPosition", {
get: function () {
var _a;
return (_a = this._headerPosition) !== null && _a !== void 0 ? _a : 'top';
},
set: function (value) {
var _a;
this._headerPosition = value;
(0, dom_1.removeClasses)(this.container, 'dv-groupview-header-top', 'dv-groupview-header-bottom', 'dv-groupview-header-left', 'dv-groupview-header-right');
(0, dom_1.addClasses)(this.container, "dv-groupview-header-".concat(value));
var direction = value === 'top' || value === 'bottom' ? 'horizontal' : 'vertical';
this.tabsContainer.direction = direction;
this.header.direction = direction;
// resize the active panel to fit the new header direction
// if not, the panel will overflow the tabs container
if ((_a = this._activePanel) === null || _a === void 0 ? void 0 : _a.layout) {
this._activePanel.layout(this._width, this._height);
}
if (this._leftHeaderActions ||
this._rightHeaderActions ||
this._prefixHeaderActions) {
this.updateHeaderActions();
}
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewGroupPanelModel.prototype, "location", {
get: function () {
return this._location;
},
set: function (value) {
var _this = this;
this._location = value;
(0, dom_1.toggleClass)(this.container, 'dv-groupview-floating', false);
(0, dom_1.toggleClass)(this.container, 'dv-groupview-popout', false);
(0, dom_1.toggleClass)(this.container, 'dv-groupview-edge', false);
// Mouse and touch drop targets must agree on accepted zones.
var applyZones = function (zones) {
_this.contentContainer.dropTarget.setTargetZones(zones);
_this.contentContainer.pointerDropTarget.setTargetZones(zones);
};
switch (value.type) {
case 'grid':
applyZones(['top', 'bottom', 'left', 'right', 'center']);
break;
case 'floating':
applyZones(['center']);
applyZones(value
? ['center']
: ['top', 'bottom', 'left', 'right', 'center']);
(0, dom_1.toggleClass)(this.container, 'dv-groupview-floating', true);
break;
case 'popout':
applyZones(['center']);
(0, dom_1.toggleClass)(this.container, 'dv-groupview-popout', true);
break;
case 'edge':
applyZones(['center']);
(0, dom_1.toggleClass)(this.container, 'dv-groupview-edge', true);
break;
}
this.groupPanel.api._onDidLocationChange.fire({
location: this.location,
});
},
enumerable: false,
configurable: true
});
DockviewGroupPanelModel.prototype._scheduleTabGroupUpdate = function () {
var _this = this;
if (this._pendingTabGroupUpdate) {
return;
}
this._pendingTabGroupUpdate = true;
queueMicrotask(function () {
_this._pendingTabGroupUpdate = false;
if (!_this.isDisposed) {
_this.tabsContainer.updateTabGroups();
}
});
};
DockviewGroupPanelModel.prototype.createTabGroup = function (options) {
var _this = this;
var _a;
var id = (_a = options === null || options === void 0 ? void 0 : options.id) !== null && _a !== void 0 ? _a : "tg-".concat(this.id, "-").concat(this._tabGroupIdCounter++);
var tabGroup = new tabGroup_1.TabGroup(id, {
label: options === null || options === void 0 ? void 0 : options.label,
color: options === null || options === void 0 ? void 0 : options.color,
collapsed: options === null || options === void 0 ? void 0 : options.collapsed,
componentParams: options === null || options === void 0 ? void 0 : options.componentParams,
});
this._tabGroups.push(tabGroup);
this._tabGroupMap.set(id, tabGroup);
this._tabGroupDisposables.set(id, new lifecycle_1.CompositeDisposable(tabGroup.onDidChange(function () {
_this._onDidTabGroupChange.fire({ tabGroup: tabGroup });
}), tabGroup.onDidCollapseChange(function (isCollapsed) {
if (isCollapsed) {
_this._handleGroupCollapse(tabGroup);
}
else {
_this._handleGroupExpand(tabGroup);
}
_this._onDidTabGroupCollapsedChange.fire({
tabGroup: tabGroup,
});
}), tabGroup.onDidDestroy(function () {
_this._removeTabGroupInternal(tabGroup);
})));
this._onDidCreateTabGroup.fire({ tabGroup: tabGroup });
return tabGroup;
};
DockviewGroupPanelModel.prototype.dissolveTabGroup = function (tabGroupId) {
var e_2, _a;
var tabGroup = this._tabGroupMap.get(tabGroupId);
if (!tabGroup) {
return;
}
// Remove all panels from the group (they stay in the flat panel list)
var panelIds = __spreadArray([], __read(tabGroup.panelIds), false);
try {
for (var panelIds_1 = __values(panelIds), panelIds_1_1 = panelIds_1.next(); !panelIds_1_1.done; panelIds_1_1 = panelIds_1.next()) {
var panelId = panelIds_1_1.value;
tabGroup.removePanel(panelId);
this._panelToTabGroup.delete(panelId);
this._onDidRemovePanelFromTabGroup.fire({ tabGroup: tabGroup, panelId: panelId });
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (panelIds_1_1 && !panelIds_1_1.done && (_a = panelIds_1.return)) _a.call(panelIds_1);
}
finally { if (e_2) throw e_2.error; }
}
tabGroup.dispose();
};
DockviewGroupPanelModel.prototype.addPanelToTabGroup = function (tabGroupId, panelId, index) {
var tabGroup = this._tabGroupMap.get(tabGroupId);
if (!tabGroup) {
return;
}
// Ensure the panel actually exists in this group model
if (!this._panels.some(function (p) { return p.id === panelId; })) {
return;
}
// Remove from any existing group first
var existingGroup = this.getTabGroupForPanel(panelId);
if (existingGroup) {
if (existingGroup.id === tabGroupId) {
return; // already in this group
}
this.removePanelFromTabGroup(panelId);
}
tabGroup.addPanel(panelId, index);
this._panelToTabGroup.set(panelId, tabGroup);
// Enforce contiguity: move the panel in the flat _panels array
// to the correct global position matching its group-local index
this._enforceContiguity(tabGroup, panelId);
this._onDidAddPanelToTabGroup.fire({ tabGroup: tabGroup, panelId: panelId });
};
/**
* Move a panel to a new index within its tab group.
* Updates both the group's panelIds order and the flat _panels array.
*/
DockviewGroupPanelModel.prototype.movePanelWithinGroup = function (tabGroupId, panelId, newIndex) {
var tabGroup = this._tabGroupMap.get(tabGroupId);
if (!tabGroup || !tabGroup.containsPanel(panelId)) {
return;
}
// Remove and re-add at new index within the group
tabGroup.removePanel(panelId);
tabGroup.addPanel(panelId, newIndex);
// Re-enforce contiguity in the flat array
this._enforceContiguity(tabGroup, panelId);
this.tabsContainer.updateTabGroups();
};
/**
* Move a panel from one tab group to another.
*/
DockviewGroupPanelModel.prototype.movePanelBetweenGroups = function (sourcePanelId, targetTabGroupId, targetIndex) {
var sourceGroup = this._findTabGroupForPanel(sourcePanelId);
var targetGroup = this._tabGroupMap.get(targetTabGroupId);
if (!targetGroup) {
return;
}
if (sourceGroup) {
sourceGroup.removePanel(sourcePanelId);
this._panelToTabGroup.delete(sourcePanelId);
this._onDidRemovePanelFromTabGroup.fire({
tabGroup: sourceGroup,
panelId: sourcePanelId,
});
// Auto-destroy empty source group
if (sourceGroup.isEmpty) {
sourceGroup.dispose();
}
}
targetGroup.addPanel(sourcePanelId, targetIndex);
this._panelToTabGroup.set(sourcePanelId, targetGroup);
this._enforceContiguity(targetGroup, sourcePanelId);
this._onDidAddPanelToTabGroup.fire({
tabGroup: targetGroup,
panelId: sourcePanelId,
});
};
/**
* Move an entire tab group to a new position in the tab bar.
* The group's internal panel order is preserved.
*/
DockviewGroupPanelModel.prototype.moveTabGroup = function (tabGroupId, targetIndex) {
var e_3, _a, _b, e_4, _c;
var _this = this;
var tabGroup = this._tabGroupMap.get(tabGroupId);
if (!tabGroup || tabGroup.panelIds.length === 0) {
return;
}
// Collect group panels in their current order
var groupPanelIds = new Set(tabGroup.panelIds);
var groupPanels = tabGroup.panelIds
.map(function (pid) { return _this._panels.find(function (p) { return p.id === pid; }); })
.filter(function (p) { return p !== undefined; });
if (groupPanels.length === 0) {
return;
}
// Count how many group panels sit before the target index so
// we can compensate after removing them from the array.
var groupPanelsBefore = 0;
for (var i = 0; i < Math.min(targetIndex, this._panels.length); i++) {
if (groupPanelIds.has(this._panels[i].id)) {
groupPanelsBefore++;
}
}
try {
// Remove group panels from the flat array
for (var groupPanels_1 = __values(groupPanels), groupPanels_1_1 = groupPanels_1.next(); !groupPanels_1_1.done; groupPanels_1_1 = groupPanels_1.next()) {
var panel = groupPanels_1_1.value;
var idx = this._panels.indexOf(panel);
if (idx !== -1) {
this._panels.splice(idx, 1);
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (groupPanels_1_1 && !groupPanels_1_1.done && (_a = groupPanels_1.return)) _a.call(groupPanels_1);
}
finally { if (e_3) throw e_3.error; }
}
// Adjust target index to account for removed panels
var adjustedIndex = targetIndex - groupPanelsBefore;
// Clamp target index to valid range after removal
var insertAt = Math.max(0, Math.min(adjustedIndex, this._panels.length));
// Insert group panels at the target position
(_b = this._panels).splice.apply(_b, __spreadArray([insertAt, 0], __read(groupPanels), false));
try {
// Rebuild the tabs container to match new order
for (var _d = __values(this._panels), _e = _d.next(); !_e.done; _e = _d.next()) {
var panel = _e.value;
this.tabsContainer.delete(panel.id);
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (_e && !_e.done && (_c = _d.return)) _c.call(_d);
}
finally { if (e_4) throw e_4.error; }
}
for (var i = 0; i < this._panels.length; i++) {
this.tabsContainer.openPanel(this._panels[i], i);
}
this.tabsContainer.updateTabGroups();
};
/**
* Ensure a panel is at the correct global index in _panels
* to maintain contiguity of its tab group members.
*/
DockviewGroupPanelModel.prototype._enforceContiguity = function (tabGroup, panelId) {
var panel = this._panels.find(function (p) { return p.id === panelId; });
if (!panel) {
return;
}
var localIndex = tabGroup.indexOfPanel(panelId);
var globalIndex = this._computeGlobalIndex(tabGroup, localIndex);
var currentIndex = this._panels.indexOf(panel);
if (currentIndex === globalIndex) {
return;
}
// Move panel in the flat array
this._panels.splice(currentIndex, 1);
var adjustedIndex = globalIndex > currentIndex ? globalIndex - 1 : globalIndex;
this._panels.splice(adjustedIndex, 0, panel);
// Reorder in the tabs container to match
this.tabsContainer.delete(panelId);
this.tabsContainer.openPanel(panel, adjustedIndex);
};
/**
* Compute the global index in _panels for a group-local index.
* Finds where the group's panels start in the flat array and offsets.
*/
DockviewGroupPanelModel.prototype._computeGlobalIndex = function (tabGroup, localIndex) {
var groupPanelIds = tabGroup.panelIds;
if (groupPanelIds.length <= 1) {
// Only one panel (the one being added), keep current position
var panel = this._panels.find(function (p) { return p.id === groupPanelIds[0]; });
return panel ? this._panels.indexOf(panel) : this._panels.length;
}
var _loop_2 = function (i) {
if (i === localIndex) {
return "continue";
}
var existingPanel = this_1._panels.find(function (p) { return p.id === groupPanelIds[i]; });
if (existingPanel) {
var existingGlobalIndex = this_1._panels.indexOf(existingPanel);
return { value: Math.max(0, existingGlobalIndex + (localIndex - i)) };
}
};
var this_1 = this;
// Find the first existing group member (other than the one at localIndex)
// to anchor the group position
for (var i = 0; i < groupPanelIds.length; i++) {
var state_1 = _loop_2(i);
if (typeof state_1 === "object")
return state_1.value;
}
return this._panels.length;
};
DockviewGroupPanelModel.prototype.removePanelFromTabGroup = function (panelId) {
var tabGroup = this._findTabGroupForPanel(panelId);
if (!tabGroup) {
return;
}
tabGroup.removePanel(panelId);
this._panelToTabGroup.delete(panelId);
this._onDidRemovePanelFromTabGroup.fire({ tabGroup: tabGroup, panelId: panelId });
// Auto-destroy empty groups
if (tabGroup.isEmpty) {
tabGroup.dispose();
}
};
DockviewGroupPanelModel.prototype.getTabGroups = function () {
return this._tabGroups;
};
DockviewGroupPanelModel.prototype.updateTabGroups = function () {
this.tabsContainer.updateTabGroups();
};
DockviewGroupPanelModel.prototype.refreshTabGroupAccent = function () {
this.tabsContainer.refreshTabGroupAccent();
};
DockviewGroupPanelModel.prototype.refreshWatermark = function () {
var _a, _b;
if (this.watermark) {
this.watermark.element.remove();
(_b = (_a = this.watermark).dispose) === null || _b === void 0 ? void 0 : _b.call(_a);
this.watermark = undefined;
}
this.updateContainer();
};
DockviewGroupPanelModel.prototype.getTabGroupForPanel = function (panelId) {
return this._findTabGroupForPanel(panelId);
};
DockviewGroupPanelModel.prototype._findTabGroupForPanel = function (panelId) {
return this._panelToTabGroup.get(panelId);
};
DockviewGroupPanelModel.prototype._removeTabGroupInternal = function (tabGroup) {
var e_5, _a;
var _this = this;
var index = this._tabGroups.indexOf(tabGroup);
if (index !== -1) {
this._tabGroups.splice(index, 1);
this._tabGroupMap.delete(tabGroup.id);
try {
for (var _b = __values(tabGroup.panelIds), _c = _b.next(); !_c.done; _c = _b.next()) {
var panelId = _c.value;
this._panelToTabGroup.delete(panelId);
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_5) throw e_5.error; }
}
this._onDidDestroyTabGroup.fire({ tabGroup: tabGroup });
// Dispose the external listeners (onDidChange, onDidCollapseChange)
// we registered on this group. We cannot dispose synchronously
// here because this method runs inside the onDidDestroy fire
// loop — disposing the CompositeDisposable that holds the
// onDidDestroy subscription would splice listeners mid-iteration.
// Schedule cleanup on the next microtask instead.
var tabGroupDisposable_1 = this._tabGroupDisposables.get(tabGroup.id);
this._tabGroupDisposables.delete(tabGroup.id);
if (tabGroupDisposable_1) {
this._pendingMicrotaskDisposables.add(tabGroupDisposable_1);
queueMicrotask(function () {
_this._pendingMicrotaskDisposables.delete(tabGroupDisposable_1);
tabGroupDisposable_1.dispose();
});
}
}
};
DockviewGroupPanelModel.prototype._handleGroupCollapse = function (tabGroup) {
if (!this._activePanel) {
return;
}
// Only act if the active panel belongs to the collapsed group
if (!tabGroup.containsPanel(this._activePanel.id)) {
return;
}
var activePanelIndex = this._panels.indexOf(this._activePanel);
// Search right first, then left, for a visible (non-collapsed-group) panel
for (var i = activePanelIndex + 1; i < this._panels.length; i++) {
var candidate = this._panels[i];
var candidateGroup = this._findTabGroupForPanel(candidate.id);
if (!candidateGroup || !candidateGroup.collapsed) {
this.doSetActivePanel(candidate);
this.updateContainer();
return;
}
}
for (var i = activePanelIndex - 1; i >= 0; i--) {
var candidate = this._panels[i];
var candidateGroup = this._findTabGroupForPanel(candidate.id);
if (!candidateGroup || !candidateGroup.collapsed) {
this.doSetActivePanel(candidate);
this.updateContainer();
return;
}
}
// All tabs are in collapsed groups — show watermark
this.contentContainer.closePanel();
this.doSetActivePanel(undefined);
this.updateContainer();
};
DockviewGroupPanelModel.prototype._handleGroupExpand = function (tabGroup) {
if (this._activePanel) {
return;
}
// Watermark is showing because all groups were collapsed.
// Activate the first panel in the newly expanded group.
var firstPanelId = tabGroup.panelIds[0];
if (firstPanelId) {
var panel = this._panels.find(function (p) { return p.id === firstPanelId; });
if (panel) {
this.doSetActivePanel(panel);
this.updateContainer();
}
}
};
/** Restore tab groups from serialized data (used by fromJSON) */
DockviewGroupPanelModel.prototype.restoreTabGroups = function (serializedGroups) {
var e_6, _a, e_7, _b, e_8, _c;
try {
// Bump counter past any restored numeric suffixes to avoid ID collisions
for (var serializedGroups_1 = __values(serializedGroups), serializedGroups_1_1 = serializedGroups_1.next(); !serializedGroups_1_1.done; serializedGroups_1_1 = serializedGroups_1.next()) {
var data = serializedGroups_1_1.value;
var match = data.id.match(/-(\d+)$/);
if (match) {
var num = parseInt(match[1], 10) + 1;
if (num > this._tabGroupIdCounter) {
this._tabGroupIdCounter = num;
}
}
}
}
catch (e_6_1) { e_6 = { error: e_6_1 }; }
finally {
try {
if (serializedGroups_1_1 && !serializedGroups_1_1.done && (_a = serializedGroups_1.return)) _a.call(serializedGroups_1);
}
finally { if (e_6) throw e_6.error; }
}
try {
for (var serializedGroups_2 = __values(serializedGroups), serializedGroups_2_1 = serializedGroups_2.next(); !serializedGroups_2_1.done; serializedGroups_2_1 = serializedGroups_2.next()) {
var data = serializedGroups_2_1.value;
var tabGroup = this.createTabGroup({
id: data.id,
label: data.label,
color: data.color,
componentParams: data.componentParams,
});
var concreteGroup = this._tabGroupMap.get(tabGroup.id);
var _loop_3 = function (panelId) {
// Only add panels that actually exist in this group model
if (this_2._panels.some(function (p) { return p.id === panelId; })) {
tabGroup.addPanel(panelId);
this_2._panelToTabGroup.set(panelId, concreteGroup);
this_2._enforceContiguity(concreteGroup, panelId);
}
};
var this_2 = this;
try {
for (var _d = (e_8 = void 0, __values(data.panelIds)), _e = _d.next(); !_e.done; _e = _d.next()) {
var panelId = _e.value;
_loop_3(panelId);
}
}
catch (e_8_1) { e_8 = { error: e_8_1 }; }
finally {
try {
if (_e && !_e.done && (_c = _d.return)) _c.call(_d);
}
finally { if (e_8) throw e_8.error; }
}
if (data.collapsed) {
tabGroup.collapse();
}
// Auto-destroy if no valid panels were added
if (tabGroup.isEmpty) {
tabGroup.dispose();
}
}
}
catch (e_7_1) { e_7 = { error: e_7_1 }; }
finally {
try {
if (serializedGroups_2_1 && !serializedGroups_2_1.done && (_b = serializedGroups_2.return)) _b.call(serializedGroups_2);
}
finally { if (e_7) throw e_7.error; }
}
};
DockviewGroupPanelModel.prototype.focusContent = function () {
this.contentContainer.element.focus();
};
Object.defineProperty(DockviewGroupPanelModel.prototype, "renderContainer", {
get: function () {
var _a;
return ((_a = this._overwriteRenderContainer) !== null && _a !== void 0 ? _a : this.accessor.overlayRenderContainer);
},
set: function (value) {
var _this = this;
this.panels.forEach(function (panel) {
_this.renderContainer.detatch(panel);
});
this._overwriteRenderContainer = value;
this.panels.forEach(function (panel) {
_this.rerender(panel);
});
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewGroupPanelModel.prototype, "dropTargetContainer", {
get: function () {
var _a;
return ((_a = this._overwriteDropTargetContainer) !== null && _a !== void 0 ? _a : this.accessor.rootDropTargetContainer);
},
set: function (value) {
this._overwriteDropTargetContainer = value;
},
enumerable: false,
configurable: true
});
DockviewGroupPanelModel.prototype.initialize = function () {
var _this = this;
if (this.options.panels) {
this.options.panels.forEach(function (panel) {
_this.doAddPanel(panel);
});
}
if (this.options.activePanel) {
this.openPanel(this.options.activePanel);
}
// must be run after the constructor otherwise this.parent may not be
// correctly initialized
this.setActive(this.isActive, true);
this.updateContainer();
this.updateHeaderActions();
};
DockviewGroupPanelModel.prototype.updateHeaderActions = function () {
if (this.accessor.options.createRightHeaderActionComponent) {
this._rightHeaderActions =
this.accessor.options.createRightHeaderActionComponent(this.groupPanel);
this._rightHeaderActionsDisposable.value = this._rightHeaderActions;
this._rightHeaderActions.init({
containerApi: this._api,
api: this.groupPanel.api,
group: this.groupPanel,
});
this.tabsContainer.setRightActionsElement(this._rightHeaderActions.element);
}
else {
this._rightHeaderActions = undefined;
this._rightHeaderActionsDisposable.dispose();
this.tabsContainer.setRightActionsElement(undefined);
}
if (this.accessor.options.createLeftHeaderActionComponent) {
this._leftHeaderActions =
this.accessor.options.createLeftHeaderActionComponent(this.groupPanel);
this._leftHeaderActionsDisposable.value = this._leftHeaderActions;
this._leftHeaderActions.init({
containerApi: this._api,
api: this.groupPanel.api,
group: this.groupPanel,
});
this.tabsContainer.setLeftActionsElement(this._leftHeaderActions.element);
}
else {
this._leftHeaderActions = undefined;
this._leftHeaderActionsDisposable.dispose();
this.tabsContainer.setLeftActionsElement(undefined);
}
if (this.accessor.options.createPrefixHeaderActionComponent) {
this._prefixHeaderActions =
this.accessor.options.createPrefixHeaderActionComponent(this.groupPanel);
this._prefixHeaderActionsDisposable.value =
this._prefixHeaderActions;
this._prefixHeaderActions.init({
containerApi: this._api,
api: this.groupPanel.api,
group: this.groupPanel,
});
this.tabsContainer.setPrefixActionsElement(this._prefixHeaderActions.element);
}
else {
this._prefixHeaderActions = undefined;
this._prefixHeaderActionsDisposable.dispose();
this.tabsContainer.setPrefixActionsElement(undefined);
}
};
DockviewGroupPanelModel.prototype.rerender = function (panel) {
this.contentContainer.renderPanel(panel, { asActive: false });
};
DockviewGroupPanelModel.prototype.indexOf = function (panel) {
return this.tabsContainer.indexOf(panel.id);
};
DockviewGroupPanelModel.prototype.toJSON = function () {
var _a;
var result = {
views: this.tabsContainer.panels,
activeView: (_a = this._activePanel) === null || _a === void 0 ? void 0 : _a.id,
id: this.id,
};
if (this.locked !== false) {
result.locked = this.locked;
}
if (this.header.hidden) {
result.hideHeader = true;
}
if (this.headerPosition !== 'top') {
result.headerPosition = this.headerPosition;
}
if (this._tabGroups.length > 0) {
result.tabGroups = this._tabGroups.map(function (tg) { return tg.toJSON(); });
}
return result;
};
DockviewGroupPanelModel.prototype.moveToNext = function (options) {
if (!options) {
options = {};
}
if (!options.panel) {
options.panel = this.activePanel;
}
var index = options.panel ? this.panels.indexOf(options.panel) : -1;
var normalizedIndex;
if (index < this.panels.length - 1) {
normalizedIndex = index + 1;
}
else if (!options.suppressRoll) {
normalizedIndex = 0;
}
else {
return;
}
this.openPanel(this.panels[normalizedIndex]);
};
DockviewGroupPanelModel.prototype.moveToPrevious = function (options) {
if (!options) {
options = {};
}
if (!options.panel) {
options.panel = this.activePanel;
}
if (!options.panel) {
return;
}
var index = this.panels.indexOf(options.panel);
var normalizedIndex;
if (index > 0) {
normalizedIndex = index - 1;
}
else if (!options.suppressRoll) {
normalizedIndex = this.panels.length - 1;
}
else {
return;
}
this.openPanel(this.panels[normalizedIndex]);
};
DockviewGroupPanelModel.prototype.containsPanel = function (panel) {
return this.panels.includes(panel);
};
DockviewGroupPanelModel.prototype.init = function (_params) {
//noop
};
DockviewGroupPanelModel.prototype.update = function (_params) {
//noop
};
DockviewGroupPanelModel.prototype.focus = function () {
var _a;
(_a = this._activePanel) === null || _a === void 0 ? void 0 : _a.focus();
};
DockviewGroupPanelModel.prototype.openPanel = function (panel, options) {
/**
* set the panel group
* add the panel
* check if group active
* check if panel active
*/
if (options === void 0) { options = {}; }
if (typeof options.index !== 'number' ||
options.index > this.panels.length) {
options.index = this.panels.length;
}
var skipSetActive = !!options.skipSetActive;
// ensure the group is updated before we fire any events
panel.updateParentGroup(this.groupPanel, {
skipSetActive: options.skipSetActive,
});
this.doAddPanel(panel, options.index, {
skipSetActive: skipSetActive,
});
if (this._activePanel === panel) {
this.contentContainer.renderPanel(panel, { asActive: true });
return;
}
if (!skipSetActive) {
this.doSetActivePanel(panel);
}
if (!options.skipSetGroupActive) {
this.accessor.doSetGroupActive(this.groupPanel);
}
if (!options.skipSetActive) {
this.updateContainer();
}
};
DockviewGroupPanelModel.prototype.removePanel = function (groupItemOrId, options) {
if (options === void 0) { options = {
skipSetActive: false,
}; }
var id = typeof groupItemOrId === 'string'
? groupItemOrId
: groupItemOrId.id;
var panelToRemove = this._panels.find(function (panel) { return panel.id === id; });
if (!panelToRemove) {
throw new Error('invalid operation');
}
return this._removePanel(panelToR