UNPKG

dockview

Version:

Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support

242 lines (241 loc) 8.91 kB
import { CompositeDisposable, MutableDisposable, } from '../lifecycle'; import { Orientation, Sizing, Splitview, } from './core/splitview'; import { Emitter } from '../events'; import { SplitviewApi } from '../api/component.api'; import { createComponent } from '../panel/componentFactory'; /** * A high-level implementation of splitview that works using 'panels' */ export class SplitviewComponent extends CompositeDisposable { constructor(element, options) { super(); this.element = element; this._disposable = new MutableDisposable(); this.panels = new Map(); this._onDidLayoutChange = new Emitter(); this.onDidLayoutChange = this._onDidLayoutChange.event; this._options = options; if (!options.components) { options.components = {}; } if (!options.frameworkComponents) { options.frameworkComponents = {}; } this.splitview = new Splitview(this.element, options); this.addDisposables(this._disposable); } get options() { return this._options; } get orientation() { return this.splitview.orientation; } get splitview() { return this._splitview; } set splitview(value) { this._splitview = value; this._disposable.value = new CompositeDisposable(this._splitview.onDidSashEnd(() => { this._onDidLayoutChange.fire(undefined); })); } get minimumSize() { return this.splitview.minimumSize; } get maximumSize() { return this.splitview.maximumSize; } get height() { return this.splitview.orientation === Orientation.HORIZONTAL ? this.splitview.orthogonalSize : this.splitview.size; } get width() { return this.splitview.orientation === Orientation.HORIZONTAL ? this.splitview.size : this.splitview.orthogonalSize; } get length() { return this.panels.size; } 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.splitview.orientation = options.orientation; } this.splitview.layout(this.splitview.size, this.splitview.orthogonalSize); } focus() { var _a; (_a = this._activePanel) === null || _a === void 0 ? void 0 : _a.focus(); } movePanel(from, to) { this.splitview.moveView(from, to); } setVisible(panel, visible) { const index = this.getPanels().indexOf(panel); this.splitview.setViewVisible(index, visible); } setActive(view, skipFocus) { this._activePanel = view; this.getPanels() .filter((v) => v !== view) .forEach((v) => { v.api._onDidActiveChange.fire({ isActive: false }); if (!skipFocus) { v.focus(); } }); view.api._onDidActiveChange.fire({ isActive: true }); if (!skipFocus) { view.focus(); } } getPanels() { return this.splitview.getViews(); } removePanel(panel, sizing) { const disposable = this.panels.get(panel.id); disposable === null || disposable === void 0 ? void 0 : disposable.dispose(); this.panels.delete(panel.id); const index = this.getPanels().findIndex((_) => _ === panel); this.splitview.removeView(index, sizing); const panels = this.getPanels(); if (panels.length > 0) { this.setActive(panels[panels.length - 1]); } } getPanel(id) { return this.getPanels().find((view) => view.id === id); } addPanel(options) { if (this.panels.has(options.id)) { throw new Error(`panel ${options.id} already exists`); } const view = createComponent(options.id, options.component, this.options.components || {}, this.options.frameworkComponents || {}, this.options.frameworkWrapper ? { createComponent: this.options.frameworkWrapper.createComponent, } : undefined); view.orientation = this.splitview.orientation; view.init({ params: options.params || {}, minimumSize: options.minimumSize, maximumSize: options.maximumSize, snap: options.snap, priority: options.priority, containerApi: new SplitviewApi(this), }); const size = typeof options.size === 'number' ? options.size : Sizing.Distribute; const index = typeof options.index === 'number' ? options.index : undefined; this.splitview.addView(view, size, index); this.doAddView(view); this.setActive(view); } /** * Resize the layout to fit the parent container */ resizeToFit() { if (!this.element.parentElement) { return; } const { width, height } = this.element.parentElement.getBoundingClientRect(); this.layout(width, height); } layout(width, height) { const [size, orthogonalSize] = this.splitview.orientation === Orientation.HORIZONTAL ? [width, height] : [height, width]; this.splitview.layout(size, orthogonalSize); } doAddView(view) { const disposable = view.api.onDidFocusChange((event) => { if (!event.isFocused) { return; } this.setActive(view, true); }); this.panels.set(view.id, disposable); } toJSON() { var _a; const views = this.splitview .getViews() .map((view, i) => { const size = this.splitview.getViewSize(i); return { size, data: view.toJSON(), snap: !!view.snap, priority: view.priority, }; }); return { views, activeView: (_a = this._activePanel) === null || _a === void 0 ? void 0 : _a.id, size: this.splitview.size, orientation: this.splitview.orientation, }; } fromJSON(serializedSplitview, deferComponentLayout = false) { const { views, orientation, size, activeView } = serializedSplitview; this.splitview.dispose(); const queue = []; this.splitview = new Splitview(this.element, { orientation, proportionalLayout: this.options.proportionalLayout, descriptor: { size, views: views.map((view) => { const data = view.data; if (this.panels.has(data.id)) { throw new Error(`panel ${data.id} already exists`); } const panel = createComponent(data.id, data.component, this.options.components || {}, this.options.frameworkComponents || {}, this.options.frameworkWrapper ? { createComponent: this.options.frameworkWrapper .createComponent, } : undefined); queue.push(() => { panel.init({ params: data.params || {}, minimumSize: data.minimumSize, maximumSize: data.maximumSize, snap: view.snap, priority: view.priority, containerApi: new SplitviewApi(this), }); }); panel.orientation = orientation; this.doAddView(panel); return { size: view.size, view: panel }; }), }, }); this.layout(this.width, this.height); if (deferComponentLayout) { setTimeout(() => { queue.forEach((f) => f()); }, 0); } else { queue.forEach((f) => f()); } if (typeof activeView === 'string') { const panel = this.getPanel(activeView); if (panel) { this.setActive(panel); } } } dispose() { Array.from(this.panels.values()).forEach((value) => { value.dispose(); }); this.panels.clear(); super.dispose(); } }