UNPKG

dockview-core

Version:

Zero dependency layout manager supporting tabs, groups, grids and splitviews for vanilla TypeScript

1,138 lines 64.7 kB
"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