dockview
Version:
Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support
186 lines (185 loc) • 6.96 kB
JavaScript
import { CompositeDisposable, } from '../../lifecycle';
import { addDisposableListener, Emitter } from '../../events';
import { MouseEventKind, Tab } from '../tab';
import { last } from '../../array';
import { getPanelData } from '../../dnd/dataTransfer';
import { Droptarget } from '../../dnd/droptarget';
import { DockviewDropTargets } from '../dnd';
export class TabsContainer extends CompositeDisposable {
constructor(accessor, group, options) {
super();
this.accessor = accessor;
this.group = group;
this.tabs = [];
this.selectedIndex = -1;
this.active = false;
this._onDrop = new Emitter();
this.onDrop = this._onDrop.event;
this.addDisposables(this._onDrop);
this._element = document.createElement('div');
this._element.className = 'tabs-and-actions-container';
this.height = options.tabHeight;
this.actionContainer = document.createElement('div');
this.actionContainer.className = 'action-container';
this.tabContainer = document.createElement('div');
this.tabContainer.className = 'tabs-container';
this.voidContainer = document.createElement('div');
this.voidContainer.className = 'void-container';
this._element.appendChild(this.tabContainer);
this._element.appendChild(this.voidContainer);
this._element.appendChild(this.actionContainer);
this.voidDropTarget = new Droptarget(this.voidContainer, {
validOverlays: 'none',
canDisplayOverlay: (event) => {
var _a;
const data = getPanelData();
if (data) {
// don't show the overlay if the tab being dragged is the last panel of this group
return ((_a = last(this.tabs)) === null || _a === void 0 ? void 0 : _a.value.panelId) !== data.panelId;
}
return group.model.canDisplayOverlay(event, DockviewDropTargets.Panel);
},
});
this.addDisposables(this.voidDropTarget.onDrop((event) => {
this._onDrop.fire({
event: event.event,
index: this.tabs.length,
});
}), this.voidDropTarget, addDisposableListener(this.tabContainer, 'mousedown', (event) => {
if (event.defaultPrevented) {
return;
}
const isLeftClick = event.button === 0;
if (isLeftClick) {
this.accessor.doSetGroupActive(this.group);
}
}));
}
get panels() {
return this.tabs.map((_) => _.value.panelId);
}
get size() {
return this.tabs.length;
}
get height() {
return this._height;
}
set height(value) {
this._height = value;
if (typeof value !== 'number') {
this.element.style.removeProperty('--dv-tabs-and-actions-container-height');
}
else {
this.element.style.setProperty('--dv-tabs-and-actions-container-height', `${value}px`);
}
}
show() {
this.element.style.display = '';
}
hide() {
this.element.style.display = 'none';
}
setActionElement(element) {
if (this.actions === element) {
return;
}
if (this.actions) {
this.actions.remove();
this.actions = undefined;
}
if (element) {
this.actionContainer.appendChild(element);
this.actions = element;
}
}
get element() {
return this._element;
}
isActive(tab) {
return (this.selectedIndex > -1 &&
this.tabs[this.selectedIndex].value === tab);
}
at(index) {
var _a;
return (_a = this.tabs[index]) === null || _a === void 0 ? void 0 : _a.value;
}
indexOf(id) {
return this.tabs.findIndex((tab) => tab.value.panelId === id);
}
setActive(isGroupActive) {
this.active = isGroupActive;
}
addTab(tab, index = this.tabs.length) {
if (index < 0 || index > this.tabs.length) {
throw new Error('invalid location');
}
this.tabContainer.insertBefore(tab.value.element, this.tabContainer.children[index]);
this.tabs = [
...this.tabs.slice(0, index),
tab,
...this.tabs.slice(index),
];
if (this.selectedIndex < 0) {
this.selectedIndex = index;
}
}
delete(id) {
const index = this.tabs.findIndex((tab) => tab.value.panelId === id);
const tabToRemove = this.tabs.splice(index, 1)[0];
const { value, disposable } = tabToRemove;
disposable.dispose();
value.element.remove();
}
setActivePanel(panel) {
this.tabs.forEach((tab) => {
const isActivePanel = panel.id === tab.value.panelId;
tab.value.setActive(isActivePanel);
});
}
openPanel(panel, index = this.tabs.length) {
var _a;
if (this.tabs.find((tab) => tab.value.panelId === panel.id)) {
return;
}
const tabToAdd = new Tab(panel.id, this.accessor, this.group);
if (!((_a = panel.view) === null || _a === void 0 ? void 0 : _a.tab)) {
throw new Error('invalid header component');
}
tabToAdd.setContent(panel.view.tab);
const disposable = CompositeDisposable.from(tabToAdd.onChanged((event) => {
var _a;
const alreadyFocused = panel.id === ((_a = this.group.model.activePanel) === null || _a === void 0 ? void 0 : _a.id) &&
this.group.model.isContentFocused();
this.accessor.fireMouseEvent(Object.assign(Object.assign({}, event), { panel, tab: true }));
const isLeftClick = event.event.button === 0;
if (!isLeftClick || event.event.defaultPrevented) {
return;
}
switch (event.kind) {
case MouseEventKind.CLICK:
this.group.model.openPanel(panel, {
skipFocus: alreadyFocused,
});
break;
}
}), tabToAdd.onDrop((event) => {
this._onDrop.fire({
event: event.event,
index: this.tabs.findIndex((x) => x.value === tabToAdd),
});
}));
const value = { value: tabToAdd, disposable };
this.addTab(value, index);
this.activePanel = panel;
}
closePanel(panel) {
this.delete(panel.id);
}
dispose() {
super.dispose();
this.tabs.forEach((tab) => {
tab.disposable.dispose();
});
this.tabs = [];
}
}