dockview-core
Version:
Zero dependency layout manager supporting tabs, groups, grids and splitviews for vanilla TypeScript
985 lines (984 loc) • 193 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 __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
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));
};
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.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DockviewComponent = void 0;
var gridview_1 = require("../gridview/gridview");
var droptarget_1 = require("../dnd/droptarget");
var array_1 = require("../array");
var dockviewPanel_1 = require("./dockviewPanel");
var lifecycle_1 = require("../lifecycle");
var events_1 = require("../events");
var watermark_1 = require("./components/watermark/watermark");
var math_1 = require("../math");
var deserializer_1 = require("./deserializer");
var options_1 = require("./options");
var baseComponentGridview_1 = require("../gridview/baseComponentGridview");
var component_api_1 = require("../api/component.api");
var splitview_1 = require("../splitview/splitview");
var dockviewGroupPanelModel_1 = require("./dockviewGroupPanelModel");
var events_2 = require("./events");
var dockviewGroupPanel_1 = require("./dockviewGroupPanel");
var dockviewPanelModel_1 = require("./dockviewPanelModel");
var dataTransfer_1 = require("../dnd/dataTransfer");
var overlay_1 = require("../overlay/overlay");
var dom_1 = require("../dom");
var floatingTitleBar_1 = require("./components/titlebar/floatingTitleBar");
var modules_1 = require("./modules");
var allModules_1 = require("./allModules");
var constants_1 = require("../constants");
var overlayRenderContainer_1 = require("../overlay/overlayRenderContainer");
var popoutWindow_1 = require("../popoutWindow");
var strictEventsSequencing_1 = require("./strictEventsSequencing");
var popupService_1 = require("./components/popupService");
var dropTargetAnchorContainer_1 = require("../dnd/dropTargetAnchorContainer");
var theme_1 = require("./theme");
var dockviewShell_1 = require("./dockviewShell");
var tabGroupAccent_1 = require("./tabGroupAccent");
function buildTabGroupColorPalette(options) {
var _a;
var entries = (_a = options.tabGroupColors) !== null && _a !== void 0 ? _a : tabGroupAccent_1.DEFAULT_TAB_GROUP_COLORS;
var enabled = options.tabGroupAccent !== 'off';
return new tabGroupAccent_1.TabGroupColorPalette(entries, enabled);
}
function moveGroupWithoutDestroying(options) {
var activePanel = options.from.activePanel;
var panels = __spreadArray([], __read(options.from.panels), false).map(function (panel) {
var removedPanel = options.from.model.removePanel(panel);
options.from.model.renderContainer.detatch(panel);
return removedPanel;
});
panels.forEach(function (panel) {
options.to.model.openPanel(panel, {
skipSetActive: activePanel !== panel,
skipSetGroupActive: true,
});
});
}
var _hasWarnedUsingCoreDirectly = false;
/**
* `dockview-core` is an internal package. The public `dockview` package calls
* `markDockviewPackageLoaded()` on import; if that marker is absent the consumer
* is using `dockview-core` directly, so emit a one-time console warning
* steering them to `dockview`.
*
* Suppressed in production builds: it is a development-time nudge and most
* bundlers inline `process.env.NODE_ENV` so the branch is dropped entirely. The
* `typeof process` guard keeps this safe in plain browser/UMD contexts where
* `process` is undefined.
*/
function warnIfUsingCoreDirectly() {
if (typeof process !== 'undefined' &&
process.env &&
process.env.NODE_ENV === 'production') {
return;
}
if (_hasWarnedUsingCoreDirectly || (0, modules_1.isDockviewPackageLoaded)()) {
return;
}
_hasWarnedUsingCoreDirectly = true;
console.warn('dockview: do not use "dockview-core" directly — it is an internal ' +
'package. Use the "dockview" package, the JavaScript version of ' +
'dockview, instead. This notice is shown once.');
}
var DockviewComponent = /** @class */ (function (_super) {
__extends(DockviewComponent, _super);
function DockviewComponent(container, options) {
var e_1, _a;
var _b, _c, _d, _e, _f;
var _this = _super.call(this, container, {
proportionalLayout: true,
orientation: splitview_1.Orientation.HORIZONTAL,
styles: options.hideBorders
? { separatorBorder: 'transparent' }
: undefined,
disableAutoResizing: options.disableAutoResizing,
locked: options.locked,
margin: (_c = (_b = options.theme) === null || _b === void 0 ? void 0 : _b.gap) !== null && _c !== void 0 ? _c : 0,
className: options.className,
}) || this;
_this.nextGroupId = (0, math_1.sequentialNumberGenerator)();
_this._deserializer = new deserializer_1.DefaultDockviewDeserialzier(_this);
_this._moduleRegistry = new modules_1.ModuleRegistry();
_this._onWillDragPanel = new events_1.Emitter();
_this.onWillDragPanel = _this._onWillDragPanel.event;
_this._onWillDragGroup = new events_1.Emitter();
_this.onWillDragGroup = _this._onWillDragGroup.event;
_this._onDidDrop = new events_1.Emitter();
_this.onDidDrop = _this._onDidDrop.event;
_this._onWillDrop = new events_1.Emitter();
_this.onWillDrop = _this._onWillDrop.event;
// Transaction boundary bracketing each top-level structural mutation.
// Compound operations (e.g. a drag that relocates a panel) nest via the
// depth counter and bracket as a single transaction. See `mutation()`.
_this._mutationDepth = 0;
// Current operation origin. Defaults to `'user'`; the DockviewApi boundary
// flips it to `'api'` for the duration of a programmatic call via
// `withOrigin`. Nested operations inherit the outermost origin (tracked by
// `_originDepth`, independent of mutation bracketing so it also covers
// active-panel changes that are not structural mutations).
_this._origin = 'user';
_this._originDepth = 0;
_this._onWillMutateLayout = new events_1.Emitter();
_this.onWillMutateLayout = _this._onWillMutateLayout.event;
_this._onDidMutateLayout = new events_1.Emitter();
_this.onDidMutateLayout = _this._onDidMutateLayout.event;
_this._onWillShowOverlay = new events_1.Emitter();
_this.onWillShowOverlay = _this._onWillShowOverlay.event;
_this._onUnhandledDragOver = new events_1.Emitter();
_this.onUnhandledDragOver = _this._onUnhandledDragOver.event;
_this._onDidRemovePanel = new events_1.Emitter();
_this.onDidRemovePanel = _this._onDidRemovePanel.event;
_this._onDidAddPanel = new events_1.Emitter();
_this.onDidAddPanel = _this._onDidAddPanel.event;
_this._onDidPopoutGroupSizeChange = new events_1.Emitter();
_this.onDidPopoutGroupSizeChange = _this._onDidPopoutGroupSizeChange.event;
_this._onDidPopoutGroupPositionChange = new events_1.Emitter();
_this.onDidPopoutGroupPositionChange = _this._onDidPopoutGroupPositionChange.event;
_this._onDidAddPopoutGroup = new events_1.Emitter();
_this.onDidAddPopoutGroup = _this._onDidAddPopoutGroup.event;
_this._onDidRemovePopoutGroup = new events_1.Emitter();
_this.onDidRemovePopoutGroup = _this._onDidRemovePopoutGroup.event;
_this._onDidOpenPopoutWindowFail = new events_1.Emitter();
_this.onDidOpenPopoutWindowFail = _this._onDidOpenPopoutWindowFail.event;
_this._onDidLayoutFromJSON = new events_1.Emitter();
_this.onDidLayoutFromJSON = _this._onDidLayoutFromJSON.event;
_this._onDidActivePanelChange = new events_1.Emitter({ replay: true });
_this.onDidActivePanelChange = _this._onDidActivePanelChange.event;
_this._onDidMovePanel = new events_1.Emitter();
_this.onDidMovePanel = _this._onDidMovePanel.event;
_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;
_this._onDidMaximizedGroupChange = new events_1.Emitter();
_this.onDidMaximizedGroupChange = _this._onDidMaximizedGroupChange.event;
_this._inShellLayout = false;
_this._onDidRemoveGroup = new events_1.Emitter();
_this.onDidRemoveGroup = _this._onDidRemoveGroup.event;
_this._onDidAddGroup = new events_1.Emitter();
_this.onDidAddGroup = _this._onDidAddGroup.event;
_this._onDidOptionsChange = new events_1.Emitter();
_this.onDidOptionsChange = _this._onDidOptionsChange.event;
_this._onDidActiveGroupChange = new events_1.Emitter();
_this.onDidActiveGroupChange = _this._onDidActiveGroupChange.event;
_this._moving = false;
_this._options = options;
_this._tabGroupColorPalette = buildTabGroupColorPalette(options);
// Internal seam: defaults to the full set, but tests can supply a
// subset to assert every module is independently removable (and the
// opt-in module API will build on this later). Not part of the public
// options surface.
var explicitModules = options
.modules;
var modules = explicitModules !== null && explicitModules !== void 0 ? explicitModules : __spreadArray(__spreadArray([], __read(allModules_1.AllModules), false), __read((0, modules_1.getRegisteredModules)()), false);
try {
for (var modules_2 = __values(modules), modules_2_1 = modules_2.next(); !modules_2_1.done; modules_2_1 = modules_2.next()) {
var module_1 = modules_2_1.value;
_this._moduleRegistry.register(module_1);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (modules_2_1 && !modules_2_1.done && (_a = modules_2.return)) _a.call(modules_2);
}
finally { if (e_1) throw e_1.error; }
}
_this._moduleRegistry.initialize(_this);
// Surface popout removal symmetrically with `onDidAddPopoutGroup`. The
// service is the single point every removal path funnels through — a
// genuine window close and an explicit removal alike — and it suppresses
// the event during component teardown.
var popoutWindowService = _this._popoutWindowService;
if (popoutWindowService) {
_this.addDisposables(popoutWindowService.onDidRemove(function (entry) {
_this._onDidRemovePopoutGroup.fire({
id: entry.popoutGroup.id,
group: entry.popoutGroup,
window: entry.getWindow(),
});
}));
}
// Purely a developer warning (no functional effect): nudge anyone using
// the internal `dockview-core` package directly towards `dockview`.
warnIfUsingCoreDirectly();
_this.popupService = new popupService_1.PopupService(_this.element);
_this._api = new component_api_1.DockviewApi(_this);
// The shell always wraps the dockview element so edge groups can be
// added at any time via addEdgeGroup() without re-parenting the DOM.
_this.disableResizing = true;
container.removeChild(_this.element);
_this._shellManager = new dockviewShell_1.ShellManager(container, _this.element, function (w, h) { return _this._layoutFromShell(w, h); }, (_e = (_d = options.theme) === null || _d === void 0 ? void 0 : _d.gap) !== null && _e !== void 0 ? _e : 0, (_f = options.theme) === null || _f === void 0 ? void 0 : _f.edgeGroupCollapsedSize);
// The shell wraps the dockview element, so move the popup anchor
// into the shell so overflow dropdowns in edge groups position correctly
_this.popupService.updateRoot(_this._shellManager.element);
_this._shellThemeClassnames = new dom_1.Classnames(_this._shellManager.element);
// Anchor the overlay container to the shell element so that edge groups
// (which live outside this.element in the shell layout) are covered when
// dndOverlayMounting is 'absolute'.
_this.rootDropTargetContainer = new dropTargetAnchorContainer_1.DropTargetAnchorContainer(_this._shellManager.element, { disabled: true });
_this.overlayRenderContainer = new overlayRenderContainer_1.OverlayRenderContainer(_this._shellManager.element, _this);
// Hosted in the shell (not inside `.dv-dockview`) so floating overlays
// share a stacking context with `dv-render-overlay` panels; sized to
// mirror the gridview rect so saved positions remain valid.
_this._floatingOverlayHost = document.createElement('div');
_this._floatingOverlayHost.className = 'dv-floating-overlay-host';
_this._shellManager.element.appendChild(_this._floatingOverlayHost);
(0, dom_1.toggleClass)(_this.gridview.element, 'dv-dockview', true);
(0, dom_1.toggleClass)(_this.element, 'dv-debug', !!options.debug);
_this.updateTheme();
if (options.debug) {
_this.addDisposables(new strictEventsSequencing_1.StrictEventsSequencing(_this));
}
_this.addDisposables(_this.rootDropTargetContainer, _this.overlayRenderContainer, _this._onWillDragPanel, _this._onWillDragGroup, _this._onWillShowOverlay, _this._onDidActivePanelChange, _this._onDidAddPanel, _this._onDidRemovePanel, _this._onDidLayoutFromJSON, _this._onDidDrop, _this._onWillDrop, _this._onWillMutateLayout, _this._onDidMutateLayout, _this._onDidMovePanel, _this._onDidMovePanel.event(function () {
/**
* Update overlay positions after DOM layout completes to prevent 0×0 dimensions.
* With defaultRenderer="always" this results in panel content not showing after move operations.
* Debounced to avoid multiple calls when moving groups with multiple panels.
*/
_this.debouncedUpdateAllPositions();
}), _this._onDidAddGroup, _this._onDidRemoveGroup, _this._onDidActiveGroupChange, _this._onUnhandledDragOver, _this._onDidMaximizedGroupChange, _this._onDidPopoutGroupSizeChange, _this._onDidPopoutGroupPositionChange, _this._onDidAddPopoutGroup, _this._onDidRemovePopoutGroup, _this._onDidOpenPopoutWindowFail, _this._onDidCreateTabGroup, _this._onDidDestroyTabGroup, _this._onDidAddPanelToTabGroup, _this._onDidRemovePanelFromTabGroup, _this._onDidTabGroupChange, _this._onDidTabGroupCollapsedChange, events_1.Event.any(_this.onDidPopoutGroupSizeChange, _this.onDidPopoutGroupPositionChange, _this.onDidCreateTabGroup, _this.onDidDestroyTabGroup, _this.onDidAddPanelToTabGroup, _this.onDidRemovePanelFromTabGroup, _this.onDidTabGroupChange, _this.onDidTabGroupCollapsedChange)(function () {
// Popout size/position and tab-group mutations persist as layout changes.
_this.fireLayoutChange();
}), _this._onDidOptionsChange, _this.onDidAdd(function (event) {
if (!_this._moving) {
_this._onDidAddGroup.fire(event);
}
}), _this.onDidRemove(function (event) {
if (!_this._moving) {
_this._onDidRemoveGroup.fire(event);
}
}), _this.onDidActiveChange(function (event) {
if (!_this._moving) {
_this._onDidActiveGroupChange.fire(event);
}
}), _this.onDidMaximizedChange(function (event) {
_this._onDidMaximizedGroupChange.fire({
group: event.panel,
isMaximized: event.isMaximized,
});
}), events_1.Event.any(_this.onDidAddPanel, _this.onDidRemovePanel, _this.onDidAddGroup, _this.onDidRemove, _this.onDidRemoveGroup, _this.onDidMovePanel, _this.onDidActivePanelChange)(function () {
_this._bufferOnDidLayoutChange.fire();
}), lifecycle_1.Disposable.from(function () {
var _a;
// Registry disposes init() subscriptions then every module
// service that implements IDisposable. The order matters so
// init handlers stop firing into services about to be torn
// down. Includes popout-restoration timer cancellation that
// resolves promises so awaiters of popoutRestorationPromise
// don't hang. See issue #851.
_this._moduleRegistry.dispose();
(_a = _this._shellManager) === null || _a === void 0 ? void 0 : _a.dispose();
}));
// Root edge-drop wiring lives with its (optional) module — guard it so
// the module is independently removable.
var rootDropTarget = _this._rootDropTargetService;
if (rootDropTarget) {
_this.addDisposables(rootDropTarget.onWillShowOverlay(function (event) {
if (_this.gridview.length > 0 &&
event.position === 'center') {
// option only available when no panels in primary grid
return;
}
_this._onWillShowOverlay.fire(new events_2.DockviewWillShowOverlayLocationEvent(event, {
kind: 'edge',
panel: undefined,
api: _this._api,
group: undefined,
getData: dataTransfer_1.getPanelData,
}));
}), rootDropTarget.onDrop(function (event) {
var _a;
var willDropEvent = new dockviewGroupPanelModel_1.DockviewWillDropEvent({
nativeEvent: event.nativeEvent,
position: event.position,
panel: undefined,
api: _this._api,
group: undefined,
getData: dataTransfer_1.getPanelData,
kind: 'edge',
});
_this._onWillDrop.fire(willDropEvent);
if (willDropEvent.defaultPrevented) {
return;
}
var data = (0, dataTransfer_1.getPanelData)();
if (data) {
_this.moveGroupOrPanel({
from: {
groupId: data.groupId,
panelId: (_a = data.panelId) !== null && _a !== void 0 ? _a : undefined,
},
to: {
group: _this.orthogonalize(event.position),
position: 'center',
},
});
}
else {
_this._onDidDrop.fire(new dockviewGroupPanelModel_1.DockviewDidDropEvent({
nativeEvent: event.nativeEvent,
position: event.position,
panel: undefined,
api: _this._api,
group: undefined,
getData: dataTransfer_1.getPanelData,
}));
}
}));
}
// Final module wiring: runs after the host is fully constructed.
// Modules subscribe to host events here so the component doesn't
// need to manually invoke them at scattered call sites.
_this._moduleRegistry.postConstruct(_this);
return _this;
}
DockviewComponent.prototype.fireDidCreateTabGroup = function (event) {
this._onDidCreateTabGroup.fire(event);
};
DockviewComponent.prototype.fireDidDestroyTabGroup = function (event) {
this._onDidDestroyTabGroup.fire(event);
};
DockviewComponent.prototype.fireDidAddPanelToTabGroup = function (event) {
this._onDidAddPanelToTabGroup.fire(event);
};
DockviewComponent.prototype.fireDidRemovePanelFromTabGroup = function (event) {
this._onDidRemovePanelFromTabGroup.fire(event);
};
DockviewComponent.prototype.fireDidTabGroupChange = function (event) {
this._onDidTabGroupChange.fire(event);
};
DockviewComponent.prototype.fireDidTabGroupCollapsedChange = function (event) {
this._onDidTabGroupCollapsedChange.fire(event);
};
Object.defineProperty(DockviewComponent.prototype, "orientation", {
get: function () {
return this.gridview.orientation;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "totalPanels", {
get: function () {
return this.panels.length;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "panels", {
get: function () {
return this.groups.flatMap(function (group) { return group.panels; });
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "options", {
get: function () {
return this._options;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "tabGroupColorPalette", {
get: function () {
return this._tabGroupColorPalette;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "activePanel", {
get: function () {
var activeGroup = this.activeGroup;
if (!activeGroup) {
return undefined;
}
return activeGroup.activePanel;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "renderer", {
get: function () {
var _a;
return (_a = this.options.defaultRenderer) !== null && _a !== void 0 ? _a : 'onlyWhenVisible';
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "defaultHeaderPosition", {
get: function () {
var _a;
return (_a = this.options.defaultHeaderPosition) !== null && _a !== void 0 ? _a : 'top';
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "api", {
get: function () {
return this._api;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "floatingGroups", {
get: function () {
var _a, _b, _c;
return ((_c = (_b = (_a = this._moduleRegistry) === null || _a === void 0 ? void 0 : _a.services.floatingGroupService) === null || _b === void 0 ? void 0 : _b.floatingGroups) !== null && _c !== void 0 ? _c : []);
},
enumerable: false,
configurable: true
});
/**
* Boxes of the floating groups other than `exclude`, in coordinates
* relative to the floating overlay container. Supplied to a
* `transformFloatingGroupDrag` callback as `context.others` so it can
* align the dragged float against its siblings.
*/
DockviewComponent.prototype._gatherFloatingGroupBoxes = function (exclude) {
var _a;
var container = (_a = this._floatingOverlayHost) !== null && _a !== void 0 ? _a : this.gridview.element;
var containerRect = container.getBoundingClientRect();
return this.floatingGroups
.filter(function (floating) { return floating.group !== exclude; })
.map(function (floating) {
var rect = floating.overlay.element.getBoundingClientRect();
return {
left: rect.left - containerRect.left,
top: rect.top - containerRect.top,
width: rect.width,
height: rect.height,
};
});
};
Object.defineProperty(DockviewComponent.prototype, "_floatingGroupService", {
get: function () {
return this._moduleRegistry.services.floatingGroupService;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "_popoutWindowService", {
get: function () {
return this._moduleRegistry.services.popoutWindowService;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "_watermarkService", {
get: function () {
// Tier 1 module — optional. Callers must `?.`-guard so the module
// can be removed from AllModules without crashing the component.
return this._moduleRegistry.services.watermarkService;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "_edgeGroupService", {
get: function () {
return this._moduleRegistry.services.edgeGroupService;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "_rootDropTargetService", {
get: function () {
// Optional like every other module service — RootDropTargetModule can be
// removed from the registered set without crashing the component.
return this._moduleRegistry.services.rootDropTargetService;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "_advancedDnDService", {
get: function () {
// Optional — callers `?.`-guard so the module can be removed from
// AllModules. Absent ⇒ the onWill* hooks simply don't fire (≡ no
// subscriber), which is invisible to apps not customising DnD.
return this._moduleRegistry.services.advancedDnDService;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "headerActionsService", {
get: function () {
return this._moduleRegistry.services.headerActionsService;
},
enumerable: false,
configurable: true
});
DockviewComponent.prototype.isGridEmpty = function () {
return this.gridview.length === 0;
};
DockviewComponent.prototype.rootDropTargetOverrideTarget = function () {
var _a;
return (_a = this.rootDropTargetContainer) === null || _a === void 0 ? void 0 : _a.model;
};
DockviewComponent.prototype.dispatchUnhandledDragOver = function (nativeEvent, position) {
var event = new options_1.DockviewUnhandledDragOverEvent(nativeEvent, 'edge', position, dataTransfer_1.getPanelData);
this._onUnhandledDragOver.fire(event);
return event.isAccepted;
};
// IAdvancedDnDHost — the emitters stay here so the public onWill* event
// shape is unchanged; AdvancedDnDService routes the per-group fires
// through these. Engine guards (e.g. disableDnd) run on the component
// ahead of the dispatch.
DockviewComponent.prototype.fireWillDragPanel = function (event) {
this._onWillDragPanel.fire(event);
};
DockviewComponent.prototype.fireWillDragGroup = function (event) {
this._onWillDragGroup.fire(event);
};
DockviewComponent.prototype.fireWillDrop = function (event) {
this._onWillDrop.fire(event);
};
DockviewComponent.prototype.fireWillShowOverlay = function (event) {
this._onWillShowOverlay.fire(event);
};
/**
* Resolve the custom group drag ghost (via the AdvancedDnD module), or
* `undefined` to fall back to the default chip. Returns `undefined` when
* the module is absent — the default ghost then renders.
*/
DockviewComponent.prototype.buildGroupDragGhost = function (group) {
var _a;
return (_a = this._advancedDnDService) === null || _a === void 0 ? void 0 : _a.buildGroupDragGhost(group);
};
/**
* Resolve the app-supplied drop overlay model (via the AdvancedDnD module)
* for a group drop target, or `undefined` to keep the target's default.
*/
DockviewComponent.prototype.resolveDropOverlayModel = function (location, group) {
var _a;
return (_a = this._advancedDnDService) === null || _a === void 0 ? void 0 : _a.resolveOverlayModel(location, group);
};
Object.defineProperty(DockviewComponent.prototype, "rootElement", {
// IAccessibilityHost — keyboard docking reaches the AdvancedDnD preview +
// LiveRegion announcer through these so the service stays decoupled.
/** Outermost element — the shell (incl. edge groups) once built, else the gridview. */
get: function () {
var _a, _b;
return (_b = (_a = this._shellManager) === null || _a === void 0 ? void 0 : _a.element) !== null && _b !== void 0 ? _b : this.element;
},
enumerable: false,
configurable: true
});
/**
* The next / previous group in gridview (spatial) order, wrapping round.
* The keyboard accessibility module's focus navigation is built on this
* primitive — the only piece that needs the grid internals; the rest of
* the navigation logic lives in the AccessibilityService.
*/
DockviewComponent.prototype.adjacentGroup = function (group, reverse) {
var _a;
// gridview traversal only covers grid groups; a floating/popout group
// isn't in the grid, so there's no adjacent grid group to step to.
if (group.api.location.type !== 'grid') {
return undefined;
}
var location = (0, gridview_1.getGridLocation)(group.element);
return ((_a = (reverse
? this.gridview.previous(location)
: this.gridview.next(location))) === null || _a === void 0 ? void 0 : _a.view);
};
/**
* The nearest grid group in a spatial direction from `group`, by
* comparing group centre points. Floating and popout groups sit outside
* the grid's geometry and are ignored. Returns `undefined` when there is
* no group in that direction.
*/
DockviewComponent.prototype.adjacentGroupInDirection = function (group, direction) {
var e_2, _a;
if (group.api.location.type !== 'grid') {
return undefined;
}
var from = group.element.getBoundingClientRect();
var fromX = from.left + from.width / 2;
var fromY = from.top + from.height / 2;
var best;
var bestDistance = Number.POSITIVE_INFINITY;
try {
for (var _b = __values(this.groups), _c = _b.next(); !_c.done; _c = _b.next()) {
var candidate = _c.value;
if (candidate === group || candidate.api.location.type !== 'grid') {
continue;
}
var rect = candidate.element.getBoundingClientRect();
var dx = rect.left + rect.width / 2 - fromX;
var dy = rect.top + rect.height / 2 - fromY;
// Require the candidate to sit predominantly in the asked-for
// direction (dominant axis), so 'left' ignores a group that's
// mostly above/below.
var inDirection = direction === 'left'
? dx < 0 && Math.abs(dx) >= Math.abs(dy)
: direction === 'right'
? dx > 0 && Math.abs(dx) >= Math.abs(dy)
: direction === 'up'
? dy < 0 && Math.abs(dy) >= Math.abs(dx)
: dy > 0 && Math.abs(dy) >= Math.abs(dx);
if (!inDirection) {
continue;
}
var distance = dx * dx + dy * dy;
if (distance < bestDistance) {
bestDistance = distance;
best = candidate;
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_2) throw e_2.error; }
}
return best;
};
DockviewComponent.prototype.showDropPreview = function (group, position) {
var _a, _b;
return ((_b = (_a = this._advancedDnDService) === null || _a === void 0 ? void 0 : _a.showPreviewOverlay(group, position)) !== null && _b !== void 0 ? _b : lifecycle_1.Disposable.NONE);
};
DockviewComponent.prototype.announce = function (message) {
var _a;
(_a = this._moduleRegistry.services.liveRegionService) === null || _a === void 0 ? void 0 : _a.announce(message);
};
DockviewComponent.prototype.dockPanel = function (panel, group, position) {
this.moveGroupOrPanel({
from: { groupId: panel.group.id, panelId: panel.id },
to: { group: group, position: position },
});
};
Object.defineProperty(DockviewComponent.prototype, "contextMenuService", {
get: function () {
// Owned by ContextMenuModule — undefined when the module is not
// registered, so callers must `?.`-guard.
return this._moduleRegistry.services.contextMenuService;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DockviewComponent.prototype, "mountElement", {
get: function () {
return this.gridview.element;
},
enumerable: false,
configurable: true
});
DockviewComponent.prototype.hasVisibleGridGroup = function () {
return this.groups.some(function (group) { return group.api.location.type === 'grid' && group.api.isVisible; });
};
DockviewComponent.prototype.fireLayoutChange = function () {
this._bufferOnDidLayoutChange.fire();
};
Object.defineProperty(DockviewComponent.prototype, "popoutRestorationPromise", {
/**
* Promise that resolves when all popout groups from the last fromJSON call are restored.
* Useful for tests that need to wait for delayed popout creation.
*/
get: function () {
var _a, _b;
return ((_b = (_a = this._popoutWindowService) === null || _a === void 0 ? void 0 : _a.restorationPromise) !== null && _b !== void 0 ? _b : Promise.resolve());
},
enumerable: false,
configurable: true
});
DockviewComponent.prototype.setVisible = function (panel, visible) {
switch (panel.api.location.type) {
case 'grid':
_super.prototype.setVisible.call(this, panel, visible);
break;
case 'floating': {
var item = this.floatingGroups.find(function (floatingGroup) { return floatingGroup.group === panel; });
if (item) {
item.overlay.setVisible(visible);
panel.api._onDidVisibilityChange.fire({
isVisible: visible,
});
}
break;
}
case 'popout':
console.warn('dockview: You cannot hide a group that is in a popout window');
break;
case 'edge':
// Edge group visibility is managed via setEdgeGroupVisible
break;
}
};
/**
* Returns the {@link PopupService} that should host popovers (context
* menus, tab overflow menus) for the given group. Popout groups have their
* own service rooted in their popout window so the popover renders there
* and dismisses on events from that window.
*/
DockviewComponent.prototype.getPopupServiceForGroup = function (group) {
var _a, _b;
return ((_b = (_a = this._popoutWindowService) === null || _a === void 0 ? void 0 : _a.getPopupService(group.id)) !== null && _b !== void 0 ? _b : this.popupService);
};
DockviewComponent.prototype.addPopoutGroup = function (itemToPopout, options) {
var _this = this;
// The transaction brackets the synchronous structural change; the
// popout window opens asynchronously after it resolves.
return this.mutation('popout', function () {
return _this._doAddPopoutGroup(itemToPopout, options);
});
};
/** Enumerate the popout groups currently open in their own windows. */
DockviewComponent.prototype.getPopouts = function () {
var _a, _b;
return ((_b = (_a = this._popoutWindowService) === null || _a === void 0 ? void 0 : _a.entries.map(function (entry) { return ({
id: entry.popoutGroup.id,
group: entry.popoutGroup,
window: entry.getWindow(),
}); })) !== null && _b !== void 0 ? _b : []);
};
DockviewComponent.prototype._doAddPopoutGroup = function (itemToPopout, options) {
var _this = this;
var _a, _b, _c, _d, _e;
var service = (0, modules_1.assertModule)(this._popoutWindowService, 'PopoutWindow', 'api.addPopoutGroup');
if (!service) {
return Promise.resolve(false);
}
if (itemToPopout instanceof dockviewGroupPanel_1.DockviewGroupPanel &&
itemToPopout.model.location.type === 'edge') {
// edge groups are permanent structural elements and cannot be popped out
return Promise.resolve(false);
}
if (itemToPopout instanceof dockviewPanel_1.DockviewPanel &&
itemToPopout.group.size === 1) {
return this.addPopoutGroup(itemToPopout.group, options);
}
var theme = (0, dom_1.getDockviewTheme)(this.gridview.element);
var element = this.element;
function getBox() {
if (options === null || options === void 0 ? void 0 : options.position) {
return options.position;
}
if (itemToPopout instanceof dockviewGroupPanel_1.DockviewGroupPanel) {
return itemToPopout.element.getBoundingClientRect();
}
if (itemToPopout.group) {
return itemToPopout.group.element.getBoundingClientRect();
}
return element.getBoundingClientRect();
}
var box = getBox();
var groupId = (_b = (_a = options === null || options === void 0 ? void 0 : options.overridePopoutGroup) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : this.getNextGroupId();
// Resolve the configured popout URL once (per-call override → global
// option). Recorded on the entry / group locations so it survives
// serialization; the '/popout.html' default is applied only when
// actually opening the window, not baked into saved layouts.
var resolvedPopoutUrl = (_c = options === null || options === void 0 ? void 0 : options.popoutUrl) !== null && _c !== void 0 ? _c : (_d = this.options) === null || _d === void 0 ? void 0 : _d.popoutUrl;
var _window = new popoutWindow_1.PopoutWindow("".concat(this.id, "-").concat(groupId), // unique id
theme !== null && theme !== void 0 ? theme : '', {
url: resolvedPopoutUrl !== null && resolvedPopoutUrl !== void 0 ? resolvedPopoutUrl : '/popout.html',
left: window.screenX + box.left,
top: window.screenY + box.top,
width: box.width,
height: box.height,
onDidOpen: options === null || options === void 0 ? void 0 : options.onDidOpen,
onWillClose: options === null || options === void 0 ? void 0 : options.onWillClose,
nonce: (_e = this.options) === null || _e === void 0 ? void 0 : _e.nonce,
});
var popoutWindowDisposable = new lifecycle_1.CompositeDisposable(_window, _window.onDidClose(function () {
popoutWindowDisposable.dispose();
}));
return _window
.open()
.then(function (popoutContainer) {
var e_3, _a;
var _b, _c, _d;
if (_window.isDisposed) {
return false;
}
var referenceGroup = (options === null || options === void 0 ? void 0 : options.referenceGroup)
? options.referenceGroup
: itemToPopout instanceof dockviewPanel_1.DockviewPanel
? itemToPopout.group
: itemToPopout;
var referenceLocation = itemToPopout.api.location.type;
/**
* The group that is being added doesn't already exist within the DOM, the most likely occurrence
* of this case is when being called from the `fromJSON(...)` method
*/
var isGroupAddedToDom = referenceGroup.element.parentElement !== null;
var group;
if (options === null || options === void 0 ? void 0 : options.overridePopoutGridview) {
// Restoring a multi-group window: the anchor group is
// already built inside the supplied gridview.
group = (_b = options.overridePopoutGroup) !== null && _b !== void 0 ? _b : referenceGroup;
}
else if (!isGroupAddedToDom) {
group = referenceGroup;
}
else if (options === null || options === void 0 ? void 0 : options.overridePopoutGroup) {
group = options.overridePopoutGroup;
}
else {
group = _this.createGroup({ id: groupId });
if (popoutContainer) {
_this._onDidAddGroup.fire(group);
}
}
if (popoutContainer === null) {
_this.handleBlockedPopout({
group: group,
referenceGroup: referenceGroup,
options: options,
popoutWindowDisposable: popoutWindowDisposable,
});
return false;
}
var gready = document.createElement('div');
gready.className = 'dv-overlay-render-container';
var overlayRenderContainer = new overlayRenderContainer_1.OverlayRenderContainer(gready, _this);
group.model.renderContainer = overlayRenderContainer;
// The popout window hosts its own gridview so it can grow into
// a nested splitview layout. The window starts with the single
// anchor group; further groups arrive via drag-and-drop. On
// restore a pre-populated gridview is supplied instead.
var popoutGridview = (_c = options === null || options === void 0 ? void 0 : options.overridePopoutGridview) !== null && _c !== void 0 ? _c : _this.createNestedGridview();
if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGridview)) {
popoutGridview.addView(group, splitview_1.Sizing.Distribute, [0]);
}
// Fill the popout window. Unlike the main grid (explicit px) and
// floating windows (CSS inside .dv-resize-container), the popout
// gridview has no sizing context, so without this it collapses
// to 0 height and nothing renders.
popoutGridview.element.style.width = '100%';
popoutGridview.element.style.height = '100%';
popoutGridview.layout(_window.window.innerWidth, _window.window.innerHeight);
// Guarded so the teardown's re-entrant paths (window close
// re-enters via the anchor's doRemoveGroup) never double-dispose.
var popoutGridviewDisposed = false;
var disposePopoutGridview = function () {
if (!popoutGridviewDisposed) {
popoutGridviewDisposed = true;
popoutGridview.dispose();
}
};
var floatingBox;
if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGroup) &&
!(options === null || options === void 0 ? void 0 : options.overridePopoutGridview) &&
isGroupAddedToDom) {
if (itemToPopout instanceof dockviewPanel_1.DockviewPanel) {
_this.movingLock(function () {
var panel = referenceGroup.model.removePanel(itemToPopout);
group.model.openPanel(panel);
});
}
else {
_this.movingLock(function () {
return moveGroupWithoutDestroying({
from: referenceGroup,
to: group,
});
});
switch (referenceLocation) {
case 'grid':
referenceGroup.api.setVisible(false);
break;
case 'floating':
case 'popout':
floatingBox = (_d = _this.floatingGroups
.find(function (value) {
return value.group.api.id ===
itemToPopout.api.id;
})) === null || _d === void 0 ? void 0 : _d.overlay.toJSON();
_this.removeGroup(referenceGroup);
break;
}
}
}
popoutContainer.classList.add('dv-dockview');
popoutContainer.style.overflow = 'hidden';
popoutContainer.appendChild(gready);
popoutContainer.appendChild(popoutGridview.element);
var anchor = document.createElement('div');
var dropTargetContainer = new dropTargetAnchorContainer_1.DropTargetAnchorContainer(anchor, { disabled: _this.rootDropTargetContainer.disabled });
popoutContainer.appendChild(anchor);
group.model.dropTargetContainer = dropTargetContainer;
// Each popout group needs its own popover service so that
// tab context menus, chip menus, and tab overflow menus
// render in the popout window (not the main window) and
// their pointerdown/keydown listeners fire on the right
// window for outside-click and Escape dismissal.
var popoutPopupService = new popupService_1.PopupService(popoutContainer, _window.window);
service.setPopupService(group.id, popoutPopupService);
popoutWindowDisposable.addDisposables(popoutPopupService, lifecycle_1.Disposable.from(function () {
service.deletePopupService(group.id);
}));
group.model.location = {
type: 'popout',
getWindow: function () { return _window.window; },
popoutUrl: resolvedPopoutUrl,
};