dockview
Version:
Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support
239 lines (238 loc) • 9.65 kB
JavaScript
import { getRelativeLocation, getGridLocation, } from './gridview';
import { Position } from '../dnd/droptarget';
import { tail, sequenceEquals } from '../array';
import { CompositeDisposable } from '../lifecycle';
import { BaseGrid, GroupChangeKind, toTarget, } from './baseComponentGridview';
import { GridviewApi } from '../api/component.api';
import { createComponent } from '../panel/componentFactory';
export class GridviewComponent extends BaseGrid {
constructor(element, options) {
super(element, {
proportionalLayout: options.proportionalLayout,
orientation: options.orientation,
styles: options.styles,
});
this._options = options;
if (!this.options.components) {
this.options.components = {};
}
if (!this.options.frameworkComponents) {
this.options.frameworkComponents = {};
}
}
get orientation() {
return this.gridview.orientation;
}
set orientation(value) {
this.gridview.orientation = value;
}
get options() {
return this._options;
}
get deserializer() {
return this._deserializer;
}
set deserializer(value) {
this._deserializer = value;
}
updateOptions(options) {
const hasOrientationChanged = typeof options.orientation === 'string' &&
this.options.orientation !== options.orientation;
this._options = Object.assign(Object.assign({}, this.options), options);
if (hasOrientationChanged) {
this.gridview.orientation = options.orientation;
}
this.layout(this.gridview.width, this.gridview.height, true);
}
removePanel(panel) {
this.removeGroup(panel);
}
/**
* Serialize the current state of the layout
*
* @returns A JSON respresentation of the layout
*/
toJSON() {
var _a;
const data = this.gridview.serialize();
return {
grid: data,
activePanel: (_a = this.activeGroup) === null || _a === void 0 ? void 0 : _a.id,
};
}
setVisible(panel, visible) {
this.gridview.setViewVisible(getGridLocation(panel.element), visible);
}
setActive(panel) {
this._groups.forEach((value, key) => {
value.value.setActive(panel === value.value);
});
}
toggleVisibility(panel) {
this.setVisible(panel, !this.isVisible(panel));
}
focus() {
var _a;
(_a = this.activeGroup) === null || _a === void 0 ? void 0 : _a.focus();
}
fromJSON(serializedGridview, deferComponentLayout) {
const { grid, activePanel } = serializedGridview;
this.gridview.clear();
this._groups.clear();
const queue = [];
this.gridview.deserialize(grid, {
fromJSON: (node) => {
const { data } = node;
const view = createComponent(data.id, data.component, this.options.components || {}, this.options.frameworkComponents || {}, this.options.frameworkComponentFactory
? {
createComponent: this.options.frameworkComponentFactory
.createComponent,
}
: undefined);
queue.push(() => view.init({
params: data.params,
minimumWidth: data.minimumWidth,
maximumWidth: data.maximumWidth,
minimumHeight: data.minimumHeight,
maximumHeight: data.maximumHeight,
priority: data.priority,
snap: !!data.snap,
containerApi: new GridviewApi(this),
isVisible: node.visible,
}));
this.registerPanel(view);
return view;
},
});
this.layout(this.width, this.height, true);
if (deferComponentLayout) {
setTimeout(() => {
queue.forEach((f) => f());
}, 0);
}
else {
queue.forEach((f) => f());
}
if (typeof activePanel === 'string') {
const panel = this.getPanel(activePanel);
if (panel) {
this.doSetGroupActive(panel);
}
}
this._onGridEvent.fire({ kind: GroupChangeKind.LAYOUT_FROM_JSON });
}
movePanel(panel, options) {
var _a;
let relativeLocation;
const removedPanel = this.gridview.remove(panel);
const referenceGroup = (_a = this._groups.get(options.reference)) === null || _a === void 0 ? void 0 : _a.value;
if (!referenceGroup) {
throw new Error(`reference group ${options.reference} does not exist`);
}
const target = toTarget(options.direction);
if (target === Position.Center) {
throw new Error(`${target} not supported as an option`);
}
else {
const location = getGridLocation(referenceGroup.element);
relativeLocation = getRelativeLocation(this.gridview.orientation, location, target);
}
this.doAddGroup(removedPanel, relativeLocation, options.size);
}
addPanel(options) {
var _a, _b;
let relativeLocation = options.location || [0];
if ((_a = options.position) === null || _a === void 0 ? void 0 : _a.reference) {
const referenceGroup = (_b = this._groups.get(options.position.reference)) === null || _b === void 0 ? void 0 : _b.value;
if (!referenceGroup) {
throw new Error(`reference group ${options.position.reference} does not exist`);
}
const target = toTarget(options.position.direction);
if (target === Position.Center) {
throw new Error(`${target} not supported as an option`);
}
else {
const location = getGridLocation(referenceGroup.element);
relativeLocation = getRelativeLocation(this.gridview.orientation, location, target);
}
}
const view = createComponent(options.id, options.component, this.options.components || {}, this.options.frameworkComponents || {}, this.options.frameworkComponentFactory
? {
createComponent: this.options.frameworkComponentFactory
.createComponent,
}
: undefined);
view.init({
params: options.params || {},
minimumWidth: options.minimumWidth,
maximumWidth: options.maximumWidth,
minimumHeight: options.minimumHeight,
maximumHeight: options.maximumHeight,
priority: options.priority,
snap: !!options.snap,
containerApi: new GridviewApi(this),
isVisible: true,
});
this.registerPanel(view);
this.doAddGroup(view, relativeLocation, options.size);
return { api: view.api };
}
registerPanel(panel) {
const disposable = new CompositeDisposable(panel.api.onDidFocusChange((event) => {
if (!event.isFocused) {
return;
}
this._groups.forEach((groupItem) => {
const group = groupItem.value;
if (group !== panel) {
group.setActive(false);
}
else {
group.setActive(true);
}
});
}));
this._groups.set(panel.id, {
value: panel,
disposable,
});
}
moveGroup(referenceGroup, groupId, target) {
const sourceGroup = this.getPanel(groupId);
if (!sourceGroup) {
throw new Error('invalid operation');
}
const referenceLocation = getGridLocation(referenceGroup.element);
const targetLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, target);
const [targetParentLocation, to] = tail(targetLocation);
const sourceLocation = getGridLocation(sourceGroup.element);
const [sourceParentLocation, from] = tail(sourceLocation);
if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
// special case when 'swapping' two views within same grid location
// if a group has one tab - we are essentially moving the 'group'
// which is equivalent to swapping two views in this case
this.gridview.moveView(sourceParentLocation, from, to);
return;
}
// source group will become empty so delete the group
const targetGroup = this.doRemoveGroup(sourceGroup, {
skipActive: true,
skipDispose: true,
});
// after deleting the group we need to re-evaulate the ref location
const updatedReferenceLocation = getGridLocation(referenceGroup.element);
const location = getRelativeLocation(this.gridview.orientation, updatedReferenceLocation, target);
this.doAddGroup(targetGroup, location);
}
removeGroup(group) {
super.removeGroup(group);
const panel = this._groups.get(group.id);
if (panel) {
panel.disposable.dispose();
this._groups.delete(group.id);
}
}
dispose() {
super.dispose();
}
}