IDE-style dockable workspace built on splitters and tab stacks. Panes can be dragged between stacks, torn off into floating windows, and the resulting arrangement is round-trippable as a JSON DockLayoutSnapshot for persistence.
input, textarea, select or contenteditable inside a pane stay text and commit nothing.Drag a pane to capture a layout.
<bs-dock-manager [(layout)]="layout">
<ng-container *bsDockManagerPane="let pane">
@switch (pane) {
@case ('panel-1') { <div class="p-3"><h3>Panel 1</h3></div> }
@case ('panel-2') { <div class="p-3"><h3>Panel 2</h3></div> }
@case ('panel-3') { <div class="p-3"><h3>Panel 3</h3></div> }
}
</ng-container>
</bs-dock-manager>
import { Component, signal } from '@angular/core';
import {
BsDockManagerComponent,
BsDockManagerPaneDirective,
DockLayoutInput,
} from '@mintplayer/ng-bootstrap/dock';
@Component({
selector: 'my-workspace',
templateUrl: './my-workspace.component.html',
imports: [BsDockManagerComponent, BsDockManagerPaneDirective],
})
export class MyWorkspaceComponent {
layout = signal<DockLayoutInput>({
root: {
kind: 'split',
direction: 'horizontal',
sizes: [1, 2],
children: [
{ kind: 'stack', panes: ['panel-1', 'panel-2'], activePane: 'panel-1' },
{ kind: 'stack', panes: ['panel-3'] },
],
},
floating: [],
titles: {
'panel-1': 'Panel 1',
'panel-2': 'Panel 2',
'panel-3': 'Panel 3',
},
});
}
// Floating panes are tear-off windows positioned in screen pixels.
// Drag a tab out of its stack — or press M then F on a focused tab —
// and the dock writes back a layout with the pane moved into the floating[] array.
layout = signal<DockLayoutInput>({
root: {
kind: 'stack',
panes: ['main'],
activePane: 'main',
},
floating: [
{
id: 'floating-inspector',
root: { kind: 'stack', panes: ['inspector'], activePane: 'inspector' },
activePane: 'inspector',
bounds: { left: 680, top: 96, width: 320, height: 220 },
},
],
titles: { main: 'Editor', inspector: 'Inspector' },
});
// Persist the user's arrangement. captureLayout() returns a fresh
// DockLayoutSnapshot you can JSON.stringify into localStorage / a DB.
readonly dockManager = viewChild(BsDockManagerComponent);
saveLayout(): void {
const snapshot = this.dockManager()?.captureLayout();
if (snapshot) localStorage.setItem('dock-layout', JSON.stringify(snapshot));
}
restoreLayout(): void {
const raw = localStorage.getItem('dock-layout');
if (raw) this.layout.set(JSON.parse(raw));
}