dockview
Version:
Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support
1,326 lines (1,312 loc) • 283 kB
JavaScript
/**
* dockview
* @version 1.0.0
* @link https://github.com/mathuo/dockview
* @license MIT
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('react-dom')) :
typeof define === 'function' && define.amd ? define(['exports', 'react', 'react-dom'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.dockview = {}, global.React, global.ReactDOM));
})(this, (function (exports, React, ReactDOM) { 'use strict';
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespace(React);
var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
function runFootnote() {
var _a, _b;
const DOCKVIEW_SUPPRESS_WATERMARK = 'DOCKVIEW_WATERMARK_SUPPRESSED';
const isTest = ((_b = (_a = window.process) === null || _a === void 0 ? void 0 : _a.env) === null || _b === void 0 ? void 0 : _b.NODE_ENV) === 'test';
if (isTest) {
return; // don't spam people tests
}
const isSuppressed = !!window[DOCKVIEW_SUPPRESS_WATERMARK];
if (!isSuppressed) {
console.log([
'dockview: https://github.com/mathuo/dockview for examples and documentation',
'dockview: https://www.npmjs.com/package/dockview',
`dockview: To suppress this message set window.${DOCKVIEW_SUPPRESS_WATERMARK}=1 before importing the dockview package`,
'',
].join('\n'));
}
}
runFootnote();
exports.Event = void 0;
(function (Event) {
Event.any = (...children) => {
return (listener) => {
const disposables = children.map((child) => child(listener));
return {
dispose: () => {
disposables.forEach((d) => {
d.dispose();
});
},
};
};
};
})(exports.Event || (exports.Event = {}));
// dumb event emitter with better typings than nodes event emitter
// https://github.com/microsoft/vscode/blob/master/src/vs/base/common/event.ts
class Emitter {
constructor(options) {
this.options = options;
this._listeners = [];
this._disposed = false;
}
get event() {
if (!this._event) {
this._event = (listener) => {
var _a;
if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.replay) && this._last !== undefined) {
listener(this._last);
}
this._listeners.length === 0;
this._listeners.push(listener);
return {
dispose: () => {
const index = this._listeners.indexOf(listener);
if (index > -1) {
this._listeners.splice(index, 1);
}
},
};
};
}
return this._event;
}
fire(e) {
this._last = e;
this._listeners.forEach((listener) => {
listener(e);
});
}
dispose() {
this._listeners = [];
this._disposed = true;
}
}
function addDisposableWindowListener(element, type, listener, options) {
element.addEventListener(type, listener, options);
return {
dispose: () => {
element.removeEventListener(type, listener);
},
};
}
function addDisposableListener(element, type, listener, options) {
element.addEventListener(type, listener, options);
return {
dispose: () => {
element.removeEventListener(type, listener);
},
};
}
exports.Disposable = void 0;
(function (Disposable) {
Disposable.NONE = {
dispose: () => {
// noop
},
};
})(exports.Disposable || (exports.Disposable = {}));
class CompositeDisposable {
constructor(...args) {
this.disposables = args;
}
static from(...args) {
return new CompositeDisposable(...args);
}
addDisposables(...args) {
args === null || args === void 0 ? void 0 : args.forEach((arg) => this.disposables.push(arg));
}
dispose() {
this.disposables.forEach((arg) => arg.dispose());
}
}
class MutableDisposable {
constructor() {
this._disposable = exports.Disposable.NONE;
}
set value(disposable) {
if (this._disposable) {
this._disposable.dispose();
}
this._disposable = disposable;
}
dispose() {
if (this._disposable) {
this._disposable.dispose();
}
}
}
function tryParseJSON(text, reviver) {
try {
return JSON.parse(text, reviver);
}
catch (err) {
console.warn('failed to parse JSON');
return undefined;
}
}
class TransferObject {
constructor() {
//
}
}
class PanelTransfer extends TransferObject {
constructor(viewId, groupId, panelId) {
super();
this.viewId = viewId;
this.groupId = groupId;
this.panelId = panelId;
}
}
class PaneTransfer extends TransferObject {
constructor(viewId, paneId) {
super();
this.viewId = viewId;
this.paneId = paneId;
}
}
const DATA_KEY = 'splitview/transfer';
const isPanelTransferEvent = (event) => {
if (!event.dataTransfer) {
return false;
}
return event.dataTransfer.types.includes(DATA_KEY);
};
exports.DragType = void 0;
(function (DragType) {
DragType["DOCKVIEW_TAB"] = "dockview_tab";
DragType["EXTERNAL"] = "external_group_drag";
})(exports.DragType || (exports.DragType = {}));
/**
* Determine whether this data belong to that of an event that was started by
* dragging a tab component
*/
const isTabDragEvent = (data) => {
return data.type === exports.DragType.DOCKVIEW_TAB;
};
/**
* Determine whether this data belong to that of an event that was started by
* a custom drag-enable component
*/
const isCustomDragEvent = (data) => {
return data.type === exports.DragType.EXTERNAL;
};
const extractData = (event) => {
if (!event.dataTransfer) {
return null;
}
const data = tryParseJSON(event.dataTransfer.getData(DATA_KEY));
if (!data) {
console.warn(`[dragEvent] ${DATA_KEY} data is missing`);
}
if (typeof data.type !== 'string') {
console.warn(`[dragEvent] invalid type ${data.type}`);
}
return data;
};
/**
* A singleton to store transfer data during drag & drop operations that are only valid within the application.
*/
class LocalSelectionTransfer {
constructor() {
// protect against external instantiation
}
static getInstance() {
return LocalSelectionTransfer.INSTANCE;
}
hasData(proto) {
return proto && proto === this.proto;
}
clearData(proto) {
if (this.hasData(proto)) {
this.proto = undefined;
this.data = undefined;
}
}
getData(proto) {
if (this.hasData(proto)) {
return this.data;
}
return undefined;
}
setData(data, proto) {
if (proto) {
this.data = data;
this.proto = proto;
}
}
}
LocalSelectionTransfer.INSTANCE = new LocalSelectionTransfer();
function getPanelData() {
const panelTransfer = LocalSelectionTransfer.getInstance();
const isPanelEvent = panelTransfer.hasData(PanelTransfer.prototype);
if (!isPanelEvent) {
return undefined;
}
return panelTransfer.getData(PanelTransfer.prototype)[0];
}
function getPaneData() {
const paneTransfer = LocalSelectionTransfer.getInstance();
const isPanelEvent = paneTransfer.hasData(PaneTransfer.prototype);
if (!isPanelEvent) {
return undefined;
}
return paneTransfer.getData(PaneTransfer.prototype)[0];
}
class SplitviewApi {
constructor(component) {
this.component = component;
}
get minimumSize() {
return this.component.minimumSize;
}
get maximumSize() {
return this.component.maximumSize;
}
get height() {
return this.component.height;
}
get width() {
return this.component.width;
}
get length() {
return this.component.length;
}
get onDidLayoutChange() {
return this.component.onDidLayoutChange;
}
get orientation() {
return this.component.orientation;
}
updateOptions(options) {
this.component.updateOptions(options);
}
removePanel(panel, sizing) {
this.component.removePanel(panel, sizing);
}
setVisible(panel, isVisible) {
this.component.setVisible(panel, isVisible);
}
getPanels() {
return this.component.getPanels();
}
focus() {
this.component.focus();
}
getPanel(id) {
return this.component.getPanel(id);
}
setActive(panel) {
this.component.setActive(panel);
}
layout(width, height) {
return this.component.layout(width, height);
}
addPanel(options) {
this.component.addPanel(options);
}
resizeToFit() {
this.component.resizeToFit();
}
movePanel(from, to) {
this.component.movePanel(from, to);
}
fromJSON(data, deferComponentLayout) {
this.component.fromJSON(data, deferComponentLayout);
}
toJSON() {
return this.component.toJSON();
}
}
class PaneviewApi {
constructor(component) {
this.component = component;
}
get width() {
return this.component.width;
}
get height() {
return this.component.height;
}
get minimumSize() {
return this.component.minimumSize;
}
get maximumSize() {
return this.component.maximumSize;
}
get onDidLayoutChange() {
return this.component.onDidLayoutChange;
}
getPanels() {
return this.component.getPanels();
}
removePanel(panel) {
this.component.removePanel(panel);
}
getPanel(id) {
return this.component.getPanel(id);
}
movePanel(from, to) {
this.component.movePanel(from, to);
}
focus() {
this.component.focus();
}
layout(width, height) {
this.component.layout(width, height);
}
addPanel(options) {
return this.component.addPanel(options);
}
resizeToFit() {
this.component.resizeToFit();
}
fromJSON(data, deferComponentLayout) {
this.component.fromJSON(data, deferComponentLayout);
}
toJSON() {
return this.component.toJSON();
}
}
class GridviewApi {
constructor(component) {
this.component = component;
}
get width() {
return this.component.width;
}
get height() {
return this.component.height;
}
get minimumHeight() {
return this.component.minimumHeight;
}
get maximumHeight() {
return this.component.maximumHeight;
}
get minimumWidth() {
return this.component.minimumWidth;
}
get maximumWidth() {
return this.component.maximumWidth;
}
get onGridEvent() {
return this.component.onGridEvent;
}
get onDidLayoutChange() {
return this.component.onDidLayoutChange;
}
get panels() {
return this.component.groups;
}
get orientation() {
return this.component.orientation;
}
set orientation(value) {
this.component.updateOptions({ orientation: value });
}
focus() {
this.component.focus();
}
layout(width, height, force = false) {
this.component.layout(width, height, force);
}
addPanel(options) {
this.component.addPanel(options);
}
removePanel(panel, sizing) {
this.component.removePanel(panel, sizing);
}
movePanel(panel, options) {
this.component.movePanel(panel, options);
}
resizeToFit() {
this.component.resizeToFit();
}
getPanel(id) {
return this.component.getPanel(id);
}
toggleVisibility(panel) {
this.component.toggleVisibility(panel);
}
setVisible(panel, visible) {
this.component.setVisible(panel, visible);
}
setActive(panel) {
this.component.setActive(panel);
}
fromJSON(data, deferComponentLayout) {
return this.component.fromJSON(data, deferComponentLayout);
}
toJSON() {
return this.component.toJSON();
}
}
class DockviewApi {
constructor(component) {
this.component = component;
}
get width() {
return this.component.width;
}
get height() {
return this.component.height;
}
get minimumHeight() {
return this.component.minimumHeight;
}
get maximumHeight() {
return this.component.maximumHeight;
}
get minimumWidth() {
return this.component.minimumWidth;
}
get maximumWidth() {
return this.component.maximumWidth;
}
get size() {
return this.component.size;
}
get totalPanels() {
return this.component.totalPanels;
}
get onGridEvent() {
return this.component.onGridEvent;
}
get onDidLayoutChange() {
return this.component.onDidLayoutChange;
}
get panels() {
return this.component.panels;
}
get groups() {
return this.component.groups;
}
get activePanel() {
return this.component.activePanel;
}
get activeGroup() {
return this.component.activeGroup;
}
getTabHeight() {
return this.component.tabHeight;
}
setTabHeight(height) {
this.component.tabHeight = height;
}
focus() {
this.component.focus();
}
getPanel(id) {
return this.component.getGroupPanel(id);
}
setActivePanel(panel) {
this.component.setActivePanel(panel);
}
layout(width, height, force = false) {
this.component.layout(width, height, force);
}
addPanel(options) {
return this.component.addPanel(options);
}
removePanel(panel) {
this.component.removePanel(panel);
}
addEmptyGroup(options) {
this.component.addEmptyGroup(options);
}
moveToNext(options) {
this.component.moveToNext(options);
}
moveToPrevious(options) {
this.component.moveToPrevious(options);
}
closeAllGroups() {
return this.component.closeAllGroups();
}
removeGroup(group) {
this.component.removeGroup(group);
}
resizeToFit() {
return this.component.resizeToFit();
}
getGroup(id) {
return this.component.getPanel(id);
}
fromJSON(data) {
this.component.fromJSON(data);
}
toJSON() {
return this.component.toJSON();
}
}
function watchElementResize(element, cb) {
const observer = new ResizeObserver((entires) => {
const firstEntry = entires[0];
cb(firstEntry);
});
observer.observe(element);
return {
dispose: () => {
observer.unobserve(element);
observer.disconnect();
},
};
}
const removeClasses = (element, ...classes) => {
for (const classname of classes) {
if (element.classList.contains(classname)) {
element.classList.remove(classname);
}
}
};
const addClasses = (element, ...classes) => {
for (const classname of classes) {
if (!element.classList.contains(classname)) {
element.classList.add(classname);
}
}
};
const toggleClass = (element, className, isToggled) => {
const hasClass = element.classList.contains(className);
if (isToggled && !hasClass) {
element.classList.add(className);
}
if (!isToggled && hasClass) {
element.classList.remove(className);
}
};
function isAncestor(testChild, testAncestor) {
while (testChild) {
if (testChild === testAncestor) {
return true;
}
testChild = testChild.parentNode;
}
return false;
}
function getElementsByTagName(tag) {
return Array.prototype.slice.call(document.getElementsByTagName(tag), 0);
}
function trackFocus(element) {
return new FocusTracker(element);
}
/**
* Track focus on an element. Ensure tabIndex is set when an HTMLElement is not focusable by default
*/
class FocusTracker extends CompositeDisposable {
constructor(element) {
super();
this._onDidFocus = new Emitter();
this.onDidFocus = this._onDidFocus.event;
this._onDidBlur = new Emitter();
this.onDidBlur = this._onDidBlur.event;
let hasFocus = isAncestor(document.activeElement, element);
let loosingFocus = false;
const onFocus = () => {
loosingFocus = false;
if (!hasFocus) {
hasFocus = true;
this._onDidFocus.fire();
}
};
const onBlur = () => {
if (hasFocus) {
loosingFocus = true;
window.setTimeout(() => {
if (loosingFocus) {
loosingFocus = false;
hasFocus = false;
this._onDidBlur.fire();
}
}, 0);
}
};
this._refreshStateHandler = () => {
const currentNodeHasFocus = isAncestor(document.activeElement, element);
if (currentNodeHasFocus !== hasFocus) {
if (hasFocus) {
onBlur();
}
else {
onFocus();
}
}
};
if (element instanceof HTMLElement) {
this.addDisposables(addDisposableListener(element, 'focus', onFocus, true));
this.addDisposables(addDisposableListener(element, 'blur', onBlur, true));
}
else {
this.addDisposables(addDisposableWindowListener(element, 'focus', onFocus, true));
this.addDisposables(addDisposableWindowListener(element, 'blur', onBlur, true));
}
}
refreshState() {
this._refreshStateHandler();
}
dispose() {
super.dispose();
this._onDidBlur.dispose();
this._onDidFocus.dispose();
}
}
const clamp = (value, min, max) => {
if (min > max) {
throw new Error(`${min} > ${max} is an invalid condition`);
}
return Math.min(max, Math.max(value, min));
};
const sequentialNumberGenerator = () => {
let value = 1;
return { next: () => (value++).toString() };
};
function tail(arr) {
if (arr.length === 0) {
throw new Error('Invalid tail call');
}
return [arr.slice(0, arr.length - 1), arr[arr.length - 1]];
}
function last(arr) {
return arr.length > 0 ? arr[arr.length - 1] : undefined;
}
function sequenceEquals(arr1, arr2) {
if (arr1.length !== arr2.length) {
return false;
}
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) {
return false;
}
}
return true;
}
/**
* Pushes an element to the start of the array, if found.
*/
function pushToStart(arr, value) {
const index = arr.indexOf(value);
if (index > -1) {
arr.splice(index, 1);
arr.unshift(value);
}
}
/**
* Pushes an element to the end of the array, if found.
*/
function pushToEnd(arr, value) {
const index = arr.indexOf(value);
if (index > -1) {
arr.splice(index, 1);
arr.push(value);
}
}
const range = (from, to) => {
const result = [];
if (typeof to !== 'number') {
to = from;
from = 0;
}
if (from <= to) {
for (let i = from; i < to; i++) {
result.push(i);
}
}
else {
for (let i = from; i > to; i--) {
result.push(i);
}
}
return result;
};
function firstIndex(array, fn) {
for (let i = 0; i < array.length; i++) {
const element = array[i];
if (fn(element)) {
return i;
}
}
return -1;
}
class ViewItem {
constructor(container, view, size, disposable) {
this.container = container;
this.view = view;
this.disposable = disposable;
this._cachedVisibleSize = undefined;
if (typeof size === 'number') {
this._size = size;
this._cachedVisibleSize = undefined;
container.classList.add('visible');
}
else {
this._size = 0;
this._cachedVisibleSize = size.cachedVisibleSize;
}
}
set size(size) {
this._size = size;
}
get size() {
return this._size;
}
get cachedVisibleSize() {
return this._cachedVisibleSize;
}
get visible() {
return typeof this._cachedVisibleSize === 'undefined';
}
setVisible(visible, size) {
var _a;
if (visible === this.visible) {
return;
}
if (visible) {
this.size = clamp((_a = this._cachedVisibleSize) !== null && _a !== void 0 ? _a : 0, this.viewMinimumSize, this.viewMaximumSize);
this._cachedVisibleSize = undefined;
}
else {
this._cachedVisibleSize =
typeof size === 'number' ? size : this.size;
this.size = 0;
}
this.container.classList.toggle('visible', visible);
if (this.view.setVisible) {
this.view.setVisible(visible);
}
}
get minimumSize() {
return this.visible ? this.view.minimumSize : 0;
}
get viewMinimumSize() {
return this.view.minimumSize;
}
get maximumSize() {
return this.visible ? this.view.maximumSize : 0;
}
get viewMaximumSize() {
return this.view.maximumSize;
}
get priority() {
return this.view.priority;
}
get snap() {
return !!this.view.snap;
}
set enabled(enabled) {
this.container.style.pointerEvents = enabled ? '' : 'none';
}
// layout(offset: number, layoutContext: TLayoutContext | undefined): void {
// this.layoutContainer(offset);
// this.view.layout(this.size, offset, layoutContext);
// }
// abstract layoutContainer(offset: number): void;
dispose() {
this.disposable.dispose();
return this.view;
}
}
/*---------------------------------------------------------------------------------------------
* Accreditation: This file is largly based upon the MIT licenced VSCode sourcecode found at:
* https://github.com/microsoft/vscode/tree/main/src/vs/base/browser/ui/splitview
*--------------------------------------------------------------------------------------------*/
exports.Orientation = void 0;
(function (Orientation) {
Orientation["HORIZONTAL"] = "HORIZONTAL";
Orientation["VERTICAL"] = "VERTICAL";
})(exports.Orientation || (exports.Orientation = {}));
exports.SashState = void 0;
(function (SashState) {
SashState[SashState["MAXIMUM"] = 0] = "MAXIMUM";
SashState[SashState["MINIMUM"] = 1] = "MINIMUM";
SashState[SashState["DISABLED"] = 2] = "DISABLED";
SashState[SashState["ENABLED"] = 3] = "ENABLED";
})(exports.SashState || (exports.SashState = {}));
exports.LayoutPriority = void 0;
(function (LayoutPriority) {
LayoutPriority["Low"] = "low";
LayoutPriority["High"] = "high";
LayoutPriority["Normal"] = "normal";
})(exports.LayoutPriority || (exports.LayoutPriority = {}));
exports.Sizing = void 0;
(function (Sizing) {
Sizing.Distribute = { type: 'distribute' };
function Split(index) {
return { type: 'split', index };
}
Sizing.Split = Split;
function Invisible(cachedVisibleSize) {
return { type: 'invisible', cachedVisibleSize };
}
Sizing.Invisible = Invisible;
})(exports.Sizing || (exports.Sizing = {}));
class Splitview {
constructor(container, options) {
this.container = container;
this.views = [];
this.sashes = [];
this._size = 0;
this._orthogonalSize = 0;
this.contentSize = 0;
this._proportions = undefined;
this._onDidSashEnd = new Emitter();
this.onDidSashEnd = this._onDidSashEnd.event;
this._onDidAddView = new Emitter();
this.onDidAddView = this._onDidAddView.event;
this._onDidRemoveView = new Emitter();
this.onDidRemoveView = this._onDidAddView.event;
this._startSnappingEnabled = true;
this._endSnappingEnabled = true;
this.resize = (index, delta, sizes = this.views.map((x) => x.size), lowPriorityIndexes, highPriorityIndexes, overloadMinDelta = Number.NEGATIVE_INFINITY, overloadMaxDelta = Number.POSITIVE_INFINITY, snapBefore, snapAfter) => {
if (index < 0 || index > this.views.length) {
return 0;
}
const upIndexes = range(index, -1);
const downIndexes = range(index + 1, this.views.length);
//
if (highPriorityIndexes) {
for (const i of highPriorityIndexes) {
pushToStart(upIndexes, i);
pushToStart(downIndexes, i);
}
}
if (lowPriorityIndexes) {
for (const i of lowPriorityIndexes) {
pushToEnd(upIndexes, i);
pushToEnd(downIndexes, i);
}
}
//
const upItems = upIndexes.map((i) => this.views[i]);
const upSizes = upIndexes.map((i) => sizes[i]);
//
const downItems = downIndexes.map((i) => this.views[i]);
const downSizes = downIndexes.map((i) => sizes[i]);
//
const minDeltaUp = upIndexes.reduce((_, i) => _ + this.views[i].minimumSize - sizes[i], 0);
const maxDeltaUp = upIndexes.reduce((_, i) => _ + this.views[i].maximumSize - sizes[i], 0);
//
const maxDeltaDown = downIndexes.length === 0
? Number.POSITIVE_INFINITY
: downIndexes.reduce((_, i) => _ + sizes[i] - this.views[i].minimumSize, 0);
const minDeltaDown = downIndexes.length === 0
? Number.NEGATIVE_INFINITY
: downIndexes.reduce((_, i) => _ + sizes[i] - this.views[i].maximumSize, 0);
//
const minDelta = Math.max(minDeltaUp, minDeltaDown);
const maxDelta = Math.min(maxDeltaDown, maxDeltaUp);
//
let snapped = false;
if (snapBefore) {
const snapView = this.views[snapBefore.index];
const visible = delta >= snapBefore.limitDelta;
snapped = visible !== snapView.visible;
snapView.setVisible(visible, snapBefore.size);
}
if (!snapped && snapAfter) {
const snapView = this.views[snapAfter.index];
const visible = delta < snapAfter.limitDelta;
snapped = visible !== snapView.visible;
snapView.setVisible(visible, snapAfter.size);
}
if (snapped) {
return this.resize(index, delta, sizes, lowPriorityIndexes, highPriorityIndexes, overloadMinDelta, overloadMaxDelta);
}
//
const tentativeDelta = clamp(delta, minDelta, maxDelta);
let actualDelta = 0;
//
let deltaUp = tentativeDelta;
for (let i = 0; i < upItems.length; i++) {
const item = upItems[i];
const size = clamp(upSizes[i] + deltaUp, item.minimumSize, item.maximumSize);
const viewDelta = size - upSizes[i];
actualDelta += viewDelta;
deltaUp -= viewDelta;
item.size = size;
}
//
let deltaDown = actualDelta;
for (let i = 0; i < downItems.length; i++) {
const item = downItems[i];
const size = clamp(downSizes[i] - deltaDown, item.minimumSize, item.maximumSize);
const viewDelta = size - downSizes[i];
deltaDown += viewDelta;
item.size = size;
}
//
return delta;
};
this._orientation = options.orientation;
this.element = this.createContainer();
this.proportionalLayout =
options.proportionalLayout === undefined
? true
: !!options.proportionalLayout;
this.viewContainer = this.createViewContainer();
this.sashContainer = this.createSashContainer();
this.element.appendChild(this.sashContainer);
this.element.appendChild(this.viewContainer);
this.container.appendChild(this.element);
this.style(options.styles);
// We have an existing set of view, add them now
if (options.descriptor) {
this._size = options.descriptor.size;
options.descriptor.views.forEach((viewDescriptor, index) => {
const sizing = viewDescriptor.visible === undefined ||
viewDescriptor.visible
? viewDescriptor.size
: {
type: 'invisible',
cachedVisibleSize: viewDescriptor.size,
};
const view = viewDescriptor.view;
this.addView(view, sizing, index, true
// true skip layout
);
});
// Initialize content size and proportions for first layout
this.contentSize = this.views.reduce((r, i) => r + i.size, 0);
this.saveProportions();
}
}
get size() {
return this._size;
}
set size(value) {
this._size = value;
}
get orthogonalSize() {
return this._orthogonalSize;
}
set orthogonalSize(value) {
this._orthogonalSize = value;
}
get length() {
return this.views.length;
}
get proportions() {
return this._proportions ? [...this._proportions] : undefined;
}
get orientation() {
return this._orientation;
}
set orientation(value) {
this._orientation = value;
const tmp = this.size;
this.size = this.orthogonalSize;
this.orthogonalSize = tmp;
removeClasses(this.element, 'horizontal', 'vertical');
this.element.classList.add(this.orientation == exports.Orientation.HORIZONTAL
? 'horizontal'
: 'vertical');
}
get minimumSize() {
return this.views.reduce((r, item) => r + item.minimumSize, 0);
}
get maximumSize() {
return this.length === 0
? Number.POSITIVE_INFINITY
: this.views.reduce((r, item) => r + item.maximumSize, 0);
}
get startSnappingEnabled() {
return this._startSnappingEnabled;
}
set startSnappingEnabled(startSnappingEnabled) {
if (this._startSnappingEnabled === startSnappingEnabled) {
return;
}
this._startSnappingEnabled = startSnappingEnabled;
this.updateSashEnablement();
}
get endSnappingEnabled() {
return this._endSnappingEnabled;
}
set endSnappingEnabled(endSnappingEnabled) {
if (this._endSnappingEnabled === endSnappingEnabled) {
return;
}
this._endSnappingEnabled = endSnappingEnabled;
this.updateSashEnablement();
}
style(styles) {
if ((styles === null || styles === void 0 ? void 0 : styles.separatorBorder) === 'transparent') {
removeClasses(this.element, 'separator-border');
this.element.style.removeProperty('--dv-separator-border');
}
else {
addClasses(this.element, 'separator-border');
if (styles === null || styles === void 0 ? void 0 : styles.separatorBorder) {
this.element.style.setProperty('--dv-separator-border', styles.separatorBorder);
}
}
}
isViewVisible(index) {
if (index < 0 || index >= this.views.length) {
throw new Error('Index out of bounds');
}
const viewItem = this.views[index];
return viewItem.visible;
}
setViewVisible(index, visible) {
if (index < 0 || index >= this.views.length) {
throw new Error('Index out of bounds');
}
toggleClass(this.container, 'visible', visible);
const viewItem = this.views[index];
toggleClass(this.container, 'visible', visible);
viewItem.setVisible(visible, viewItem.size);
this.distributeEmptySpace(index);
this.layoutViews();
this.saveProportions();
}
getViewSize(index) {
if (index < 0 || index >= this.views.length) {
return -1;
}
return this.views[index].size;
}
resizeView(index, size) {
if (index < 0 || index >= this.views.length) {
return;
}
const indexes = range(this.views.length).filter((i) => i !== index);
const lowPriorityIndexes = [
...indexes.filter((i) => this.views[i].priority === exports.LayoutPriority.Low),
index,
];
const highPriorityIndexes = indexes.filter((i) => this.views[i].priority === exports.LayoutPriority.High);
const item = this.views[index];
size = Math.round(size);
size = clamp(size, item.minimumSize, Math.min(item.maximumSize, this._size));
item.size = size;
this.relayout(lowPriorityIndexes, highPriorityIndexes);
}
getViews() {
return this.views.map((x) => x.view);
}
onDidChange(item, size) {
const index = this.views.indexOf(item);
if (index < 0 || index >= this.views.length) {
return;
}
size = typeof size === 'number' ? size : item.size;
size = clamp(size, item.minimumSize, item.maximumSize);
item.size = size;
this.relayout([index]);
}
addView(view, size = { type: 'distribute' }, index = this.views.length, skipLayout) {
const container = document.createElement('div');
container.className = 'view';
container.appendChild(view.element);
let viewSize;
if (typeof size === 'number') {
viewSize = size;
}
else if (size.type === 'split') {
viewSize = this.getViewSize(size.index) / 2;
}
else if (size.type === 'invisible') {
viewSize = { cachedVisibleSize: size.cachedVisibleSize };
}
else {
viewSize = view.minimumSize;
}
const disposable = view.onDidChange((newSize) => this.onDidChange(viewItem, newSize));
const dispose = () => {
disposable === null || disposable === void 0 ? void 0 : disposable.dispose();
this.viewContainer.removeChild(container);
};
const viewItem = new ViewItem(container, view, viewSize, { dispose });
if (index === this.views.length) {
this.viewContainer.appendChild(container);
}
else {
this.viewContainer.insertBefore(container, this.viewContainer.children.item(index));
}
this.views.splice(index, 0, viewItem);
if (this.views.length > 1) {
//add sash
const sash = document.createElement('div');
sash.className = 'sash';
const onStart = (event) => {
for (const item of this.views) {
item.enabled = false;
}
const iframes = [
...getElementsByTagName('iframe'),
...getElementsByTagName('webview'),
];
for (const iframe of iframes) {
iframe.style.pointerEvents = 'none';
}
const start = this._orientation === exports.Orientation.HORIZONTAL
? event.clientX
: event.clientY;
const sashIndex = firstIndex(this.sashes, (s) => s.container === sash);
//
const sizes = this.views.map((x) => x.size);
//
let snapBefore;
let snapAfter;
const upIndexes = range(sashIndex, -1);
const downIndexes = range(sashIndex + 1, this.views.length);
const minDeltaUp = upIndexes.reduce((r, i) => r + (this.views[i].minimumSize - sizes[i]), 0);
const maxDeltaUp = upIndexes.reduce((r, i) => r + (this.views[i].viewMaximumSize - sizes[i]), 0);
const maxDeltaDown = downIndexes.length === 0
? Number.POSITIVE_INFINITY
: downIndexes.reduce((r, i) => r + (sizes[i] - this.views[i].minimumSize), 0);
const minDeltaDown = downIndexes.length === 0
? Number.NEGATIVE_INFINITY
: downIndexes.reduce((r, i) => r +
(sizes[i] - this.views[i].viewMaximumSize), 0);
const minDelta = Math.max(minDeltaUp, minDeltaDown);
const maxDelta = Math.min(maxDeltaDown, maxDeltaUp);
const snapBeforeIndex = this.findFirstSnapIndex(upIndexes);
const snapAfterIndex = this.findFirstSnapIndex(downIndexes);
if (typeof snapBeforeIndex === 'number') {
const snappedViewItem = this.views[snapBeforeIndex];
const halfSize = Math.floor(snappedViewItem.viewMinimumSize / 2);
snapBefore = {
index: snapBeforeIndex,
limitDelta: snappedViewItem.visible
? minDelta - halfSize
: minDelta + halfSize,
size: snappedViewItem.size,
};
}
if (typeof snapAfterIndex === 'number') {
const snappedViewItem = this.views[snapAfterIndex];
const halfSize = Math.floor(snappedViewItem.viewMinimumSize / 2);
snapAfter = {
index: snapAfterIndex,
limitDelta: snappedViewItem.visible
? maxDelta + halfSize
: maxDelta - halfSize,
size: snappedViewItem.size,
};
}
//
const mousemove = (mousemoveEvent) => {
const current = this._orientation === exports.Orientation.HORIZONTAL
? mousemoveEvent.clientX
: mousemoveEvent.clientY;
const delta = current - start;
this.resize(sashIndex, delta, sizes, undefined, undefined, minDelta, maxDelta, snapBefore, snapAfter);
this.distributeEmptySpace();
this.layoutViews();
};
const end = () => {
for (const item of this.views) {
item.enabled = true;
}
for (const iframe of iframes) {
iframe.style.pointerEvents = 'auto';
}
this.saveProportions();
document.removeEventListener('mousemove', mousemove);
document.removeEventListener('mouseup', end);
document.removeEventListener('mouseend', end);
this._onDidSashEnd.fire(undefined);
};
document.addEventListener('mousemove', mousemove);
document.addEventListener('mouseup', end);
document.addEventListener('mouseend', end);
};
sash.addEventListener('mousedown', onStart);
const sashItem = {
container: sash,
disposable: () => {
sash.removeEventListener('mousedown', onStart);
this.sashContainer.removeChild(sash);
},
};
this.sashContainer.appendChild(sash);
this.sashes.push(sashItem);
}
if (!skipLayout) {
this.relayout([index]);
}
if (!skipLayout &&
typeof size !== 'number' &&
size.type === 'distribute') {
this.distributeViewSizes();
}
this._onDidAddView.fire(view);
}
distributeViewSizes() {
const flexibleViewItems = [];
let flexibleSize = 0;
for (const item of this.views) {
if (item.maximumSize - item.minimumSize > 0) {
flexibleViewItems.push(item);
flexibleSize += item.size;
}
}
const size = Math.floor(flexibleSize / flexibleViewItems.length);
for (const item of flexibleViewItems) {
item.size = clamp(size, item.minimumSize, item.maximumSize);
}
const indexes = range(this.views.length);
const lowPriorityIndexes = indexes.filter((i) => this.views[i].priority === exports.LayoutPriority.Low);
const highPriorityIndexes = indexes.filter((i) => this.views[i].priority === exports.LayoutPriority.High);
this.relayout(lowPriorityIndexes, highPriorityIndexes);
}
removeView(index, sizing, skipLayout = false) {
// Remove view
const viewItem = this.views.splice(index, 1)[0];
viewItem.dispose();
// Remove sash
if (this.views.length >= 1) {
const sashIndex = Math.max(index - 1, 0);
const sashItem = this.sashes.splice(sashIndex, 1)[0];
sashItem.disposable();
}
if (!skipLayout) {
this.relayout();
}
if (sizing && sizing.type === 'distribute') {
this.distributeViewSizes();
}
this._onDidRemoveView.fire(viewItem.view);
return viewItem.view;
}
getViewCachedVisibleSize(index) {
if (index < 0 || index >= this.views.length) {
throw new Error('Index out of bounds');
}
const viewItem = this.views[index];
return viewItem.cachedVisibleSize;
}
moveView(from, to) {
const cachedVisibleSize = this.getViewCachedVisibleSize(from);
const sizing = typeof cachedVisibleSize === 'undefined'
? this.getViewSize(from)
: exports.Sizing.Invisible(cachedVisibleSize);
const view = this.removeView(from, undefined, true);
this.addView(view, sizing, to);
}
layout(size, orthogonalSize) {
const previousSize = Math.max(this.size, this.contentSize);
this.size = size;
this.orthogonalSize = orthogonalSize;
if (!this.proportions) {