dockview-core
Version: 
Zero dependency layout manager supporting tabs, grids and splitviews
1,050 lines • 94.4 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 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 dockviewFloatingGroupPanel_1 = require("./dockviewFloatingGroupPanel");
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 DEFAULT_ROOT_OVERLAY_MODEL = {
    activationSize: { type: 'pixels', value: 10 },
    size: { type: 'pixels', value: 20 },
};
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 DockviewComponent = /** @class */ (function (_super) {
    __extends(DockviewComponent, _super);
    function DockviewComponent(container, options) {
        var _a, _b, _c;
        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: (_b = (_a = options.theme) === null || _a === void 0 ? void 0 : _a.gap) !== null && _b !== void 0 ? _b : 0,
            className: options.className,
        }) || this;
        _this.nextGroupId = (0, math_1.sequentialNumberGenerator)();
        _this._deserializer = new deserializer_1.DefaultDockviewDeserialzier(_this);
        _this._watermark = null;
        _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;
        _this._onWillShowOverlay = new events_1.Emitter();
        _this.onWillShowOverlay = _this._onWillShowOverlay.event;
        _this._onUnhandledDragOverEvent = new events_1.Emitter();
        _this.onUnhandledDragOverEvent = _this._onUnhandledDragOverEvent.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._onDidLayoutFromJSON = new events_1.Emitter();
        _this.onDidLayoutFromJSON = _this._onDidLayoutFromJSON.event;
        _this._onDidActivePanelChange = new events_1.Emitter();
        _this.onDidActivePanelChange = _this._onDidActivePanelChange.event;
        _this._onDidMovePanel = new events_1.Emitter();
        _this.onDidMovePanel = _this._onDidMovePanel.event;
        _this._onDidMaximizedGroupChange = new events_1.Emitter();
        _this.onDidMaximizedGroupChange = _this._onDidMaximizedGroupChange.event;
        _this._floatingGroups = [];
        _this._popoutGroups = [];
        _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.popupService = new popupService_1.PopupService(_this.element);
        _this._themeClassnames = new dom_1.Classnames(_this.element);
        _this._api = new component_api_1.DockviewApi(_this);
        _this.rootDropTargetContainer = new dropTargetAnchorContainer_1.DropTargetAnchorContainer(_this.element, { disabled: true });
        _this.overlayRenderContainer = new overlayRenderContainer_1.OverlayRenderContainer(_this.gridview.element, _this);
        _this._rootDropTarget = new droptarget_1.Droptarget(_this.element, {
            className: 'dv-drop-target-edge',
            canDisplayOverlay: function (event, position) {
                var data = (0, dataTransfer_1.getPanelData)();
                if (data) {
                    if (data.viewId !== _this.id) {
                        return false;
                    }
                    if (position === 'center') {
                        // center drop target is only allowed if there are no panels in the grid
                        // floating panels are allowed
                        return _this.gridview.length === 0;
                    }
                    return true;
                }
                if (position === 'center' && _this.gridview.length !== 0) {
                    /**
                     * for external events only show the four-corner drag overlays, disable
                     * the center position so that external drag events can fall through to the group
                     * and panel drop target handlers
                     */
                    return false;
                }
                var firedEvent = new options_1.DockviewUnhandledDragOverEvent(event, 'edge', position, dataTransfer_1.getPanelData);
                _this._onUnhandledDragOverEvent.fire(firedEvent);
                return firedEvent.isAccepted;
            },
            acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
            overlayModel: (_c = options.rootOverlayModel) !== null && _c !== void 0 ? _c : DEFAULT_ROOT_OVERLAY_MODEL,
            getOverrideTarget: function () { var _a; return (_a = _this.rootDropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; },
        });
        _this.updateDropTargetModel(options);
        (0, dom_1.toggleClass)(_this.gridview.element, 'dv-dockview', true);
        (0, dom_1.toggleClass)(_this.element, 'dv-debug', !!options.debug);
        _this.updateTheme();
        _this.updateWatermark();
        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._onDidMovePanel, _this._onDidAddGroup, _this._onDidRemoveGroup, _this._onDidActiveGroupChange, _this._onUnhandledDragOverEvent, _this._onDidMaximizedGroupChange, _this._onDidOptionsChange, _this._onDidPopoutGroupSizeChange, _this._onDidPopoutGroupPositionChange, _this.onDidViewVisibilityChangeMicroTaskQueue(function () {
            _this.updateWatermark();
        }), _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.onDidAdd, _this.onDidRemove)(function () {
            _this.updateWatermark();
        }), events_1.Event.any(_this.onDidAddPanel, _this.onDidRemovePanel, _this.onDidAddGroup, _this.onDidRemove, _this.onDidMovePanel, _this.onDidActivePanelChange, _this.onDidPopoutGroupPositionChange, _this.onDidPopoutGroupSizeChange)(function () {
            _this._bufferOnDidLayoutChange.fire();
        }), lifecycle_1.Disposable.from(function () {
            var e_1, _a, e_2, _b;
            try {
                // iterate over a copy of the array since .dispose() mutates the original array
                for (var _c = __values(__spreadArray([], __read(_this._floatingGroups), false)), _d = _c.next(); !_d.done; _d = _c.next()) {
                    var group = _d.value;
                    group.dispose();
                }
            }
            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; }
            }
            try {
                // iterate over a copy of the array since .dispose() mutates the original array
                for (var _e = __values(__spreadArray([], __read(_this._popoutGroups), false)), _f = _e.next(); !_f.done; _f = _e.next()) {
                    var group = _f.value;
                    group.disposable.dispose();
                }
            }
            catch (e_2_1) { e_2 = { error: e_2_1 }; }
            finally {
                try {
                    if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
                }
                finally { if (e_2) throw e_2.error; }
            }
        }), _this._rootDropTarget, _this._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 dockviewGroupPanelModel_1.WillShowOverlayLocationEvent(event, {
                kind: 'edge',
                panel: undefined,
                api: _this._api,
                group: undefined,
                getData: dataTransfer_1.getPanelData,
            }));
        }), _this._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,
                }));
            }
        }), _this._rootDropTarget);
        return _this;
    }
    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, "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, "api", {
        get: function () {
            return this._api;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(DockviewComponent.prototype, "floatingGroups", {
        get: function () {
            return this._floatingGroups;
        },
        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;
        }
    };
    DockviewComponent.prototype.addPopoutGroup = function (itemToPopout, options) {
        var _this = this;
        var _a, _b, _c, _d, _e;
        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();
        var _window = new popoutWindow_1.PopoutWindow("".concat(this.id, "-").concat(groupId), // unique id
        theme !== null && theme !== void 0 ? theme : '', {
            url: (_e = (_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) !== null && _e !== void 0 ? _e : '/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,
        });
        var popoutWindowDisposable = new lifecycle_1.CompositeDisposable(_window, _window.onDidClose(function () {
            popoutWindowDisposable.dispose();
        }));
        return _window
            .open()
            .then(function (popoutContainer) {
            var _a;
            if (_window.isDisposed) {
                return false;
            }
            if (popoutContainer === null) {
                popoutWindowDisposable.dispose();
                return false;
            }
            var gready = document.createElement('div');
            gready.className = 'dv-overlay-render-container';
            var overlayRenderContainer = new overlayRenderContainer_1.OverlayRenderContainer(gready, _this);
            var 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 occurance
             * of this case is when being called from the `fromJSON(...)` method
             */
            var isGroupAddedToDom = referenceGroup.element.parentElement !== null;
            var group;
            if (!isGroupAddedToDom) {
                group = referenceGroup;
            }
            else if (options === null || options === void 0 ? void 0 : options.overridePopoutGroup) {
                group = options.overridePopoutGroup;
            }
            else {
                group = _this.createGroup({ id: groupId });
                _this._onDidAddGroup.fire(group);
            }
            group.model.renderContainer = overlayRenderContainer;
            group.layout(_window.window.innerWidth, _window.window.innerHeight);
            var floatingBox;
            if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGroup) && 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 = (_a = _this._floatingGroups
                                .find(function (value) {
                                return value.group.api.id ===
                                    itemToPopout.api.id;
                            })) === null || _a === void 0 ? void 0 : _a.overlay.toJSON();
                            _this.removeGroup(referenceGroup);
                            break;
                    }
                }
            }
            popoutContainer.classList.add('dv-dockview');
            popoutContainer.style.overflow = 'hidden';
            popoutContainer.appendChild(gready);
            popoutContainer.appendChild(group.element);
            var anchor = document.createElement('div');
            var dropTargetContainer = new dropTargetAnchorContainer_1.DropTargetAnchorContainer(anchor, { disabled: _this.rootDropTargetContainer.disabled });
            popoutContainer.appendChild(anchor);
            group.model.dropTargetContainer = dropTargetContainer;
            group.model.location = {
                type: 'popout',
                getWindow: function () { return _window.window; },
                popoutUrl: options === null || options === void 0 ? void 0 : options.popoutUrl,
            };
            if (isGroupAddedToDom &&
                itemToPopout.api.location.type === 'grid') {
                itemToPopout.api.setVisible(false);
            }
            _this.doSetGroupAndPanelActive(group);
            popoutWindowDisposable.addDisposables(group.api.onDidActiveChange(function (event) {
                var _a;
                if (event.isActive) {
                    (_a = _window.window) === null || _a === void 0 ? void 0 : _a.focus();
                }
            }), group.api.onWillFocus(function () {
                var _a;
                (_a = _window.window) === null || _a === void 0 ? void 0 : _a.focus();
            }));
            var returnedGroup;
            var isValidReferenceGroup = isGroupAddedToDom &&
                referenceGroup &&
                _this.getPanel(referenceGroup.id);
            var value = {
                window: _window,
                popoutGroup: group,
                referenceGroup: isValidReferenceGroup
                    ? referenceGroup.id
                    : undefined,
                disposable: {
                    dispose: function () {
                        popoutWindowDisposable.dispose();
                        return returnedGroup;
                    },
                },
            };
            var _onDidWindowPositionChange = (0, dom_1.onDidWindowMoveEnd)(_window.window);
            popoutWindowDisposable.addDisposables(_onDidWindowPositionChange, (0, dom_1.onDidWindowResizeEnd)(_window.window, function () {
                _this._onDidPopoutGroupSizeChange.fire({
                    width: _window.window.innerWidth,
                    height: _window.window.innerHeight,
                    group: group,
                });
            }), _onDidWindowPositionChange.event(function () {
                _this._onDidPopoutGroupPositionChange.fire({
                    screenX: _window.window.screenX,
                    screenY: _window.window.screenX,
                    group: group,
                });
            }), 
            /**
             * ResizeObserver seems slow here, I do not know why but we don't need it
             * since we can reply on the window resize event as we will occupy the full
             * window dimensions
             */
            (0, events_1.addDisposableListener)(_window.window, 'resize', function () {
                group.layout(_window.window.innerWidth, _window.window.innerHeight);
            }), overlayRenderContainer, lifecycle_1.Disposable.from(function () {
                if (_this.isDisposed) {
                    return; // cleanup may run after instance is disposed
                }
                if (isGroupAddedToDom &&
                    _this.getPanel(referenceGroup.id)) {
                    _this.movingLock(function () {
                        return moveGroupWithoutDestroying({
                            from: group,
                            to: referenceGroup,
                        });
                    });
                    if (!referenceGroup.api.isVisible) {
                        referenceGroup.api.setVisible(true);
                    }
                    if (_this.getPanel(group.id)) {
                        _this.doRemoveGroup(group, {
                            skipPopoutAssociated: true,
                        });
                    }
                }
                else if (_this.getPanel(group.id)) {
                    group.model.renderContainer =
                        _this.overlayRenderContainer;
                    group.model.dropTargetContainer =
                        _this.rootDropTargetContainer;
                    returnedGroup = group;
                    var alreadyRemoved = !_this._popoutGroups.find(function (p) { return p.popoutGroup === group; });
                    if (alreadyRemoved) {
                        /**
                         * If this popout group was explicitly removed then we shouldn't run the additional
                         * steps. To tell if the running of this disposable is the result of this popout group
                         * being explicitly removed we can check if this popout group is still referenced in
                         * the `this._popoutGroups` list.
                         */
                        return;
                    }
                    if (floatingBox) {
                        _this.addFloatingGroup(group, {
                            height: floatingBox.height,
                            width: floatingBox.width,
                            position: floatingBox,
                        });
                    }
                    else {
                        _this.doRemoveGroup(group, {
                            skipDispose: true,
                            skipActive: true,
                            skipPopoutReturn: true,
                        });
                        group.model.location = { type: 'grid' };
                        _this.movingLock(function () {
                            // suppress group add events since the group already exists
                            _this.doAddGroup(group, [0]);
                        });
                    }
                    _this.doSetGroupAndPanelActive(group);
                }
            }));
            _this._popoutGroups.push(value);
            _this.updateWatermark();
            return true;
        })
            .catch(function (err) {
            console.error('dockview: failed to create popout window', err);
            return false;
        });
    };
    DockviewComponent.prototype.addFloatingGroup = function (item, options) {
        var _this = this;
        var _a, _b, _c, _d, _e;
        var group;
        if (item instanceof dockviewPanel_1.DockviewPanel) {
            group = this.createGroup();
            this._onDidAddGroup.fire(group);
            this.movingLock(function () {
                return _this.removePanel(item, {
                    removeEmptyGroup: true,
                    skipDispose: true,
                    skipSetActiveGroup: true,
                });
            });
            this.movingLock(function () {
                return group.model.openPanel(item, { skipSetGroupActive: true });
            });
        }
        else {
            group = item;
            var popoutReferenceGroupId = (_a = this._popoutGroups.find(function (_) { return _.popoutGroup === group; })) === null || _a === void 0 ? void 0 : _a.referenceGroup;
            var popoutReferenceGroup_1 = popoutReferenceGroupId
                ? this.getPanel(popoutReferenceGroupId)
                : undefined;
            var skip = typeof (options === null || options === void 0 ? void 0 : options.skipRemoveGroup) === 'boolean' &&
                options.skipRemoveGroup;
            if (!skip) {
                if (popoutReferenceGroup_1) {
                    this.movingLock(function () {
                        return moveGroupWithoutDestroying({
                            from: item,
                            to: popoutReferenceGroup_1,
                        });
                    });
                    this.doRemoveGroup(item, {
                        skipPopoutReturn: true,
                        skipPopoutAssociated: true,
                    });
                    this.doRemoveGroup(popoutReferenceGroup_1, {
                        skipDispose: true,
                    });
                    group = popoutReferenceGroup_1;
                }
                else {
                    this.doRemoveGroup(item, {
                        skipDispose: true,
                        skipPopoutReturn: true,
                        skipPopoutAssociated: false,
                    });
                }
            }
        }
        function getAnchoredBox() {
            if (options === null || options === void 0 ? void 0 : options.position) {
                var result = {};
                if ('left' in options.position) {
                    result.left = Math.max(options.position.left, 0);
                }
                else if ('right' in options.position) {
                    result.right = Math.max(options.position.right, 0);
                }
                else {
                    result.left = constants_1.DEFAULT_FLOATING_GROUP_POSITION.left;
                }
                if ('top' in options.position) {
                    result.top = Math.max(options.position.top, 0);
                }
                else if ('bottom' in options.position) {
                    result.bottom = Math.max(options.position.bottom, 0);
                }
                else {
                    result.top = constants_1.DEFAULT_FLOATING_GROUP_POSITION.top;
                }
                if (typeof options.width === 'number') {
                    result.width = Math.max(options.width, 0);
                }
                else {
                    result.width = constants_1.DEFAULT_FLOATING_GROUP_POSITION.width;
                }
                if (typeof options.height === 'number') {
                    result.height = Math.max(options.height, 0);
                }
                else {
                    result.height = constants_1.DEFAULT_FLOATING_GROUP_POSITION.height;
                }
                return result;
            }
            return {
                left: typeof (options === null || options === void 0 ? void 0 : options.x) === 'number'
                    ? Math.max(options.x, 0)
                    : constants_1.DEFAULT_FLOATING_GROUP_POSITION.left,
                top: typeof (options === null || options === void 0 ? void 0 : options.y) === 'number'
                    ? Math.max(options.y, 0)
                    : constants_1.DEFAULT_FLOATING_GROUP_POSITION.top,
                width: typeof (options === null || options === void 0 ? void 0 : options.width) === 'number'
                    ? Math.max(options.width, 0)
                    : constants_1.DEFAULT_FLOATING_GROUP_POSITION.width,
                height: typeof (options === null || options === void 0 ? void 0 : options.height) === 'number'
                    ? Math.max(options.height, 0)
                    : constants_1.DEFAULT_FLOATING_GROUP_POSITION.height,
            };
        }
        var anchoredBox = getAnchoredBox();
        var overlay = new overlay_1.Overlay(__assign(__assign({ container: this.gridview.element, content: group.element }, anchoredBox), { minimumInViewportWidth: this.options.floatingGroupBounds === 'boundedWithinViewport'
                ? undefined
                : (_c = (_b = this.options.floatingGroupBounds) === null || _b === void 0 ? void 0 : _b.minimumWidthWithinViewport) !== null && _c !== void 0 ? _c : constants_1.DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE, minimumInViewportHeight: this.options.floatingGroupBounds === 'boundedWithinViewport'
                ? undefined
                : (_e = (_d = this.options.floatingGroupBounds) === null || _d === void 0 ? void 0 : _d.minimumHeightWithinViewport) !== null && _e !== void 0 ? _e : constants_1.DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE }));
        var el = group.element.querySelector('.dv-void-container');
        if (!el) {
            throw new Error('failed to find drag handle');
        }
        overlay.setupDrag(el, {
            inDragMode: typeof (options === null || options === void 0 ? void 0 : options.inDragMode) === 'boolean'
                ? options.inDragMode
                : false,
        });
        var floatingGroupPanel = new dockviewFloatingGroupPanel_1.DockviewFloatingGroupPanel(group, overlay);
        var disposable = new lifecycle_1.CompositeDisposable(group.api.onDidActiveChange(function (event) {
            if (event.isActive) {
                overlay.bringToFront();
            }
        }), (0, dom_1.watchElementResize)(group.element, function (entry) {
            var _a = entry.contentRect, width = _a.width, height = _a.height;
            group.layout(width, height); // let the group know it's size is changing so it can fire events to the panel
        }));
        floatingGroupPanel.addDisposables(overlay.onDidChange(function () {
            // this is either a resize or a move
            // to inform the panels .layout(...) the group with it's current size
            // don't care about resize since the above watcher handles that
            group.layout(group.width, group.height);
        }), overlay.onDidChangeEnd(function () {
            _this._bufferOnDidLayoutChange.fire();
        }), group.onDidChange(function (event) {
            overlay.setBounds({
                height: event === null || event === void 0 ? void 0 : event.height,
                width: event === null || event === void 0 ? void 0 : event.width,
            });
        }), {
            dispose: function () {
                disposable.dispose();
                (0, array_1.remove)(_this._floatingGroups, floatingGroupPanel);
                group.model.location = { type: 'grid' };
                _this.updateWatermark();
            },
        });
        this._floatingGroups.push(floatingGroupPanel);
        group.model.location = { type: 'floating' };
        if (!(options === null || options === void 0 ? void 0 : options.skipActiveGroup)) {
            this.doSetGroupAndPanelActive(group);
        }
        this.updateWatermark();
    };
    DockviewComponent.prototype.orthogonalize = function (position, options) {
        switch (position) {
            case 'top':
            case 'bottom':
                if (this.gridview.orientation === splitview_1.Orientation.HORIZONTAL) {
                    // we need to add to a vertical splitview but the current root is a horizontal splitview.
                    // insert a vertical splitview at the root level and add the existing view as a child
                    this.gridview.insertOrthogonalSplitviewAtRoot();
                }
                break;
            case 'left':
            case 'right':
                if (this.gridview.orientation === splitview_1.Orientation.VERTICAL) {
                    // we need to add to a horizontal splitview but the current root is a vertical splitview.
                    // insert a horiziontal splitview at the root level and add the existing view as a child
                    this.gridview.insertOrthogonalSplitviewAtRoot();
                }
                break;
            default:
                break;
        }
        switch (position) {
            case 'top':
            case 'left':
            case 'center':
                return this.createGroupAtLocation([0], undefined, options); // insert into first position
            case 'bottom':
            case 'right':
                return this.createGroupAtLocation([this.gridview.length], undefined, options); // insert into last position
            default:
                throw new Error("unsupported position ".concat(position));
        }
    };
    DockviewComponent.prototype.updateOptions = function (options) {
        var e_3, _a;
        var _b, _c;
        _super.prototype.updateOptions.call(this, options);
        if ('floatingGroupBounds' in options) {
            try {
                for (var _d = __values(this._floatingGroups), _e = _d.next(); !_e.done; _e = _d.next()) {
                    var group = _e.value;
                    switch (options.floatingGroupBounds) {
                        case 'boundedWithinViewport':
                            group.overlay.minimumInViewportHeight = undefined;
                            group.overlay.minimumInViewportWidth = undefined;
                            break;
                        case undefined:
                            group.overlay.minimumInViewportHeight =
                                constants_1.DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE;
                            group.overlay.minimumInViewportWidth =
                                constants_1.DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE;
                            break;
                        default:
                            group.overlay.minimumInViewportHeight =
                                (_b = options.floatingGroupBounds) === null || _b === void 0 ? void 0 : _b.minimumHeightWithinViewport;
                            group.overlay.minimumInViewportWidth =
                                (_c = options.floatingGroupBounds) === null || _c === void 0 ? void 0 : _c.minimumWidthWithinViewport;
                    }
                    group.overlay.setBounds();
                }
            }
            catch (e_3_1) { e_3 = { error: e_3_1 }; }
            finally {
                try {
                    if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
                }
                finally { if (e_3) throw e_3.error; }
            }
        }
        this.updateDropTargetModel(options);
        this._options = __assign(__assign({}, this.options), options);
        if ('theme' in options) {
            this.updateTheme();
        }
        this.layout(this.gridview.width, this.gridview.height, true);
    };
    DockviewComponent.prototype.layout = function (width, height, forceResize) {
        var e_4, _a;
        _super.prototype.layout.call(this, width, height, forceResize);
        if (this._floatingGroups) {
            try {
                for (var _b = __values(this._floatingGroups), _c = _b.next(); !_c.done; _c = _b.next()) {
                    var floating = _c.value;
                    // ensure floting groups stay within visible boundaries
                    floating.overlay.setBounds();
                }
            }
            catch (e_4_1) { e_4 = { error: e_4_1 }; }
            finally {
                try {
                    if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
                }
                finally { if (e_4) throw e_4.error; }
            }
        }
    };
    DockviewComponent.prototype.focus = function () {
        var _a;
        (_a = this.activeGroup) === null || _a === void 0 ? void 0 : _a.focus();
    };
    DockviewComponent.prototype.getGroupPanel = function (id) {
        return this.panels.find(function (panel) { return panel.id === id; });
    };
    DockviewComponent.prototype.setActivePanel = function (panel) {
        panel.group.model.openPanel(panel);
        this.doSetGroupAndPanelActive(panel.group);
    };
    DockviewComponent.prototype.moveToNext = function (options) {
        var _a;
        if (options === void 0) { options = {}; }
        if (!options.group) {
            if (!this.activeGroup) {
                return;
            }
            options.group = this.activeGroup;
        }
        if (options.includePanel && options.group) {
            if (options.group.activePanel !==
                options.group.panels[options.group.panels.length - 1]) {
                options.group.model.moveToNext({ suppressRoll: true });
                return;
            }
        }
        var location = (0, gridview_1.getGridLocation)(options.group.element);
        var next = (_a = this.gridview.next(location)) === null || _a === void 0 ? void 0 : _a.view;
        this.doSetGroupAndPanelActive(next);
    };
    DockviewComponent.prototype.moveToPrevious = function (options) {
        var _a;
        if (options === void 0) { options = {}; }
        if (!options.group) {
            if (!this.activeGroup) {
                return;
            }
            options.group = this.activeGroup;
        }
        if (options.includePanel && options.group) {
            if (options.group.activePanel !== options.group.panels[0]) {
                options.group.model.moveToPrevious({ suppressRoll: true });
                return;
            }
        }
        var location = (0, gridview_1.getGridLocation)(options.group.element);
        var next = (_a = this.gridview.previous(location)) === null || _a === void 0 ? void 0 : _a.view;
        if (next) {
            this.doSetGroupAndPanelActive(next);
        }
    };
    /**
     * Serialize the current state of the layout
     *
     * @returns A JSON respresentation of the layout
     */
    DockviewComponent.prototype.toJSON = function () {
        var _a;
        var data = this.gridview.serialize();
        var panels = this.panels.reduce(function (collection, panel) {
            collection[panel.id] = panel.toJSON();
            return collection;
        }, {});
        var floats = this._floatingGroups.map(function (group) {
            return {
                data: group.group.toJSON(),
                position: group.overlay.toJSON(),
            };
        });
        var popoutGroups = this._popoutGroups.map(function (group) {
            return {
                data: group.popoutGroup.toJSON(),
                gridReferenceGroup: group.referenceGroup,
                position: group.window.dimensions(),
                url: group.popoutGroup.api.location.type === 'popout'
                    ? group.popoutGroup.api.location.popoutUrl
                    : undefined,
            };
        });
        var result = {
            grid: data,
            panels: panels,
            activeGroup: (_a = this.activeGroup) === null || _a === void 0 ? void 0 : _a.id,
        };
        if (floats.length > 0) {
            result.floatingGroups = floats;
        }
        if (popoutGroups.length > 0) {
            result.popoutGroups = popoutGroups;
        }
        return result;
    };
    DockviewComponent.prototype.fromJSON = function (data) {
        var e_5, _a, e_6, _b, e_7, _c, e_8, _d, e_9, _e, e_10, _f, e_11, _g;
        var _this = this;
        var _h, _j, _k;
        this.clear();
        if (typeof data !== 'object' || data === null) {
            throw new Error('serialized layout must be a non-null object');
        }
        var grid = data.grid, panels = data.panels, activeGroup = data.activeGroup;
        if (grid.root.type !== 'branch' || !Array.isArray(grid.root.data)) {
            throw new Error('root must be of type branch');
        }
        try {
            // take note of the existing dimensions
            var width = this.width;
            var height = this.height;
            var createGroupFromSerializedState_1 = function (data) {
                var e_12, _a;
                var id = data.id, locked = data.locked, hideHeader = data.hideHeader, views = data.views, activeView = data.activeView;
                if (typeof id !== 'string') {
                    throw new Error('group id must be of type string');
                }
                var group = _this.createGroup({
                    id: id,
                    locked: !!locked,
                    hideHeader: !!hideHeader,
                });
                _this._onDidAddGroup.fire(group);
                var createdPanels = [];
                try {
                    for (var views_1 = __values(views), views_1_1 = views_1.next(); !views_1_1.done; views_1_1 = views_1.next()) {
                        var child = views_1_1.value;
                        /**
                         * Run the deserializer step seperately since this may fail to due corrupted external state.
                         * In running this section first we avoid firing lots of 'add' events in the event of a failure
                         * due to a corruption of input data.
                         */
                        var panel = _this._deserializer.fromJSON(panels[child], group);
                        createdPanels.push(panel);
                    }
                }
                catch (e_12_1) { e_12 = { error: e_12_1 }; }
                finally {
                    try {
                        if (views_1_1 && !views_1_1.done && (_a = views_1.return)) _a.call(views_1);
                    }
                    finally { if (e_12) throw e_12.error; }
                }
                for (var i = 0; i < views.length; i++) {
                    var panel = createdPanels[i];
                    var isActive = typeof activeView === 'string' &&
                        activeView === panel.id;
                    group.model.openPanel(panel, {
                        skipSetActive: !isActive,
                        skipSetGroupActive: true,
                    });
                }
                if (!group.activePanel && group.panels.length > 0) {
                    group.model.openPanel(group.panels[group.panels.length - 1], {
                        skipSetGroupActive: true,
                    });
                }
                return group;
            };
            this.gridview.deserialize(grid, {
                fromJSON: function (node) {
                    return createGroupFromSerializedState_1(node.data);
                },
            });
            this.layout(width, height, true);
            var serializedFloatingGroups = (_h = data.floatingGroups) !== null && _h !== void 0 ? _h : [];
            try {
                for (var serializedFloatingGroups_1 = __values(serializedFloatingGroups), serializedFloatingGroups_1_1 = serializedFloatingGroups_1.next()