UNPKG

dockview-core

Version:

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

985 lines (984 loc) 193 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 __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, };