mirror of
https://github.com/mathuo/dockview
synced 2025-02-02 06:25:44 +00:00
feat: experimental work
This commit is contained in:
parent
25489bf48e
commit
97e8a95c2b
@ -679,6 +679,239 @@ describe('dockviewComponent', () => {
|
|||||||
expect(viewQuery.length).toBe(1);
|
expect(viewQuery.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('serialization', () => {
|
||||||
|
test('reuseExistingPanels true', () => {
|
||||||
|
const parts: PanelContentPartTest[] = [];
|
||||||
|
|
||||||
|
dockview = new DockviewComponent(container, {
|
||||||
|
createComponent(options) {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
const part = new PanelContentPartTest(
|
||||||
|
options.id,
|
||||||
|
options.name
|
||||||
|
);
|
||||||
|
parts.push(part);
|
||||||
|
return part;
|
||||||
|
default:
|
||||||
|
throw new Error(`unsupported`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
dockview.layout(1000, 1000);
|
||||||
|
|
||||||
|
dockview.addPanel({ id: 'panel1', component: 'default' });
|
||||||
|
dockview.addPanel({ id: 'panel2', component: 'default' });
|
||||||
|
dockview.addPanel({ id: 'panel7', component: 'default' });
|
||||||
|
|
||||||
|
expect(parts.length).toBe(3);
|
||||||
|
|
||||||
|
expect(parts.map((part) => part.isDisposed)).toEqual([
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
dockview.fromJSON(
|
||||||
|
{
|
||||||
|
activeGroup: 'group-1',
|
||||||
|
grid: {
|
||||||
|
root: {
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel1'],
|
||||||
|
id: 'group-1',
|
||||||
|
activeView: 'panel1',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel2', 'panel3'],
|
||||||
|
id: 'group-2',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel4'],
|
||||||
|
id: 'group-3',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 1000,
|
||||||
|
},
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
orientation: Orientation.VERTICAL,
|
||||||
|
},
|
||||||
|
panels: {
|
||||||
|
panel1: {
|
||||||
|
id: 'panel1',
|
||||||
|
contentComponent: 'default',
|
||||||
|
tabComponent: 'tab-default',
|
||||||
|
title: 'panel1',
|
||||||
|
},
|
||||||
|
panel2: {
|
||||||
|
id: 'panel2',
|
||||||
|
contentComponent: 'default',
|
||||||
|
title: 'panel2',
|
||||||
|
},
|
||||||
|
panel3: {
|
||||||
|
id: 'panel3',
|
||||||
|
contentComponent: 'default',
|
||||||
|
title: 'panel3',
|
||||||
|
renderer: 'onlyWhenVisible',
|
||||||
|
},
|
||||||
|
panel4: {
|
||||||
|
id: 'panel4',
|
||||||
|
contentComponent: 'default',
|
||||||
|
title: 'panel4',
|
||||||
|
renderer: 'always',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ reuseExistingPanels: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(parts.map((part) => part.isDisposed)).toEqual([
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reuseExistingPanels false', () => {
|
||||||
|
const parts: PanelContentPartTest[] = [];
|
||||||
|
|
||||||
|
dockview = new DockviewComponent(container, {
|
||||||
|
createComponent(options) {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
const part = new PanelContentPartTest(
|
||||||
|
options.id,
|
||||||
|
options.name
|
||||||
|
);
|
||||||
|
parts.push(part);
|
||||||
|
return part;
|
||||||
|
default:
|
||||||
|
throw new Error(`unsupported`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
dockview.layout(1000, 1000);
|
||||||
|
|
||||||
|
dockview.addPanel({ id: 'panel1', component: 'default' });
|
||||||
|
dockview.addPanel({ id: 'panel2', component: 'default' });
|
||||||
|
dockview.addPanel({ id: 'panel7', component: 'default' });
|
||||||
|
|
||||||
|
expect(parts.length).toBe(3);
|
||||||
|
|
||||||
|
expect(parts.map((part) => part.isDisposed)).toEqual([
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
dockview.fromJSON({
|
||||||
|
activeGroup: 'group-1',
|
||||||
|
grid: {
|
||||||
|
root: {
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel1'],
|
||||||
|
id: 'group-1',
|
||||||
|
activeView: 'panel1',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel2', 'panel3'],
|
||||||
|
id: 'group-2',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel4'],
|
||||||
|
id: 'group-3',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 1000,
|
||||||
|
},
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
orientation: Orientation.VERTICAL,
|
||||||
|
},
|
||||||
|
panels: {
|
||||||
|
panel1: {
|
||||||
|
id: 'panel1',
|
||||||
|
contentComponent: 'default',
|
||||||
|
tabComponent: 'tab-default',
|
||||||
|
title: 'panel1',
|
||||||
|
},
|
||||||
|
panel2: {
|
||||||
|
id: 'panel2',
|
||||||
|
contentComponent: 'default',
|
||||||
|
title: 'panel2',
|
||||||
|
},
|
||||||
|
panel3: {
|
||||||
|
id: 'panel3',
|
||||||
|
contentComponent: 'default',
|
||||||
|
title: 'panel3',
|
||||||
|
renderer: 'onlyWhenVisible',
|
||||||
|
},
|
||||||
|
panel4: {
|
||||||
|
id: 'panel4',
|
||||||
|
contentComponent: 'default',
|
||||||
|
title: 'panel4',
|
||||||
|
renderer: 'always',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(parts.map((part) => part.isDisposed)).toEqual([
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('serialization', () => {
|
test('serialization', () => {
|
||||||
dockview.layout(1000, 1000);
|
dockview.layout(1000, 1000);
|
||||||
|
|
||||||
|
@ -209,6 +209,10 @@ export class TestPanel implements IDockviewPanel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateFromStateModel(state: GroupviewPanelState): void {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
init(params: IGroupPanelInitParameters) {
|
init(params: IGroupPanelInitParameters) {
|
||||||
this._params = params;
|
this._params = params;
|
||||||
}
|
}
|
||||||
|
@ -854,8 +854,11 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
|
|||||||
/**
|
/**
|
||||||
* Create a component from a serialized object.
|
* Create a component from a serialized object.
|
||||||
*/
|
*/
|
||||||
fromJSON(data: SerializedDockview): void {
|
fromJSON(
|
||||||
this.component.fromJSON(data);
|
data: SerializedDockview,
|
||||||
|
options?: { reuseExistingPanels: boolean }
|
||||||
|
): void {
|
||||||
|
this.component.fromJSON(data, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -147,6 +147,7 @@ type MoveGroupOrPanelOptions = {
|
|||||||
position: Position;
|
position: Position;
|
||||||
index?: number;
|
index?: number;
|
||||||
};
|
};
|
||||||
|
keepEmptyGroups?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface FloatingGroupOptions {
|
export interface FloatingGroupOptions {
|
||||||
@ -219,6 +220,7 @@ export interface IDockviewComponent extends IBaseGrid<DockviewGroupPanel> {
|
|||||||
onWillClose?: (event: { id: string; window: Window }) => void;
|
onWillClose?: (event: { id: string; window: Window }) => void;
|
||||||
}
|
}
|
||||||
): Promise<boolean>;
|
): Promise<boolean>;
|
||||||
|
fromJSON(data: any, options?: { reuseExistingPanels: boolean }): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DockviewComponent
|
export class DockviewComponent
|
||||||
@ -381,17 +383,17 @@ export class DockviewComponent
|
|||||||
this.updateWatermark();
|
this.updateWatermark();
|
||||||
}),
|
}),
|
||||||
this.onDidAdd((event) => {
|
this.onDidAdd((event) => {
|
||||||
if (!this._moving) {
|
if (!this._isEventSuppressionEnabled) {
|
||||||
this._onDidAddGroup.fire(event);
|
this._onDidAddGroup.fire(event);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
this.onDidRemove((event) => {
|
this.onDidRemove((event) => {
|
||||||
if (!this._moving) {
|
if (!this._isEventSuppressionEnabled) {
|
||||||
this._onDidRemoveGroup.fire(event);
|
this._onDidRemoveGroup.fire(event);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
this.onDidActiveChange((event) => {
|
this.onDidActiveChange((event) => {
|
||||||
if (!this._moving) {
|
if (!this._isEventSuppressionEnabled) {
|
||||||
this._onDidActiveGroupChange.fire(event);
|
this._onDidActiveGroupChange.fire(event);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@ -675,13 +677,13 @@ export class DockviewComponent
|
|||||||
|
|
||||||
if (!options?.overridePopoutGroup && isGroupAddedToDom) {
|
if (!options?.overridePopoutGroup && isGroupAddedToDom) {
|
||||||
if (itemToPopout instanceof DockviewPanel) {
|
if (itemToPopout instanceof DockviewPanel) {
|
||||||
this.movingLock(() => {
|
this.runWithSuppressedEvents(() => {
|
||||||
const panel =
|
const panel =
|
||||||
referenceGroup.model.removePanel(itemToPopout);
|
referenceGroup.model.removePanel(itemToPopout);
|
||||||
group.model.openPanel(panel);
|
group.model.openPanel(panel);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.movingLock(() =>
|
this.runWithSuppressedEvents(() =>
|
||||||
moveGroupWithoutDestroying({
|
moveGroupWithoutDestroying({
|
||||||
from: referenceGroup,
|
from: referenceGroup,
|
||||||
to: group,
|
to: group,
|
||||||
@ -773,7 +775,7 @@ export class DockviewComponent
|
|||||||
isGroupAddedToDom &&
|
isGroupAddedToDom &&
|
||||||
this.getPanel(referenceGroup.id)
|
this.getPanel(referenceGroup.id)
|
||||||
) {
|
) {
|
||||||
this.movingLock(() =>
|
this.runWithSuppressedEvents(() =>
|
||||||
moveGroupWithoutDestroying({
|
moveGroupWithoutDestroying({
|
||||||
from: group,
|
from: group,
|
||||||
to: referenceGroup,
|
to: referenceGroup,
|
||||||
@ -830,7 +832,7 @@ export class DockviewComponent
|
|||||||
group = this.createGroup();
|
group = this.createGroup();
|
||||||
this._onDidAddGroup.fire(group);
|
this._onDidAddGroup.fire(group);
|
||||||
|
|
||||||
this.movingLock(() =>
|
this.runWithSuppressedEvents(() =>
|
||||||
this.removePanel(item, {
|
this.removePanel(item, {
|
||||||
removeEmptyGroup: true,
|
removeEmptyGroup: true,
|
||||||
skipDispose: true,
|
skipDispose: true,
|
||||||
@ -838,7 +840,7 @@ export class DockviewComponent
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.movingLock(() =>
|
this.runWithSuppressedEvents(() =>
|
||||||
group.model.openPanel(item, { skipSetGroupActive: true })
|
group.model.openPanel(item, { skipSetGroupActive: true })
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -857,7 +859,7 @@ export class DockviewComponent
|
|||||||
|
|
||||||
if (!skip) {
|
if (!skip) {
|
||||||
if (popoutReferenceGroup) {
|
if (popoutReferenceGroup) {
|
||||||
this.movingLock(() =>
|
this.runWithSuppressedEvents(() =>
|
||||||
moveGroupWithoutDestroying({
|
moveGroupWithoutDestroying({
|
||||||
from: item,
|
from: item,
|
||||||
to: popoutReferenceGroup,
|
to: popoutReferenceGroup,
|
||||||
@ -955,7 +957,7 @@ export class DockviewComponent
|
|||||||
const el = group.element.querySelector('.dv-void-container');
|
const el = group.element.querySelector('.dv-void-container');
|
||||||
|
|
||||||
if (!el) {
|
if (!el) {
|
||||||
throw new Error('failed to find drag handle');
|
throw new Error('dockview: failed to find drag handle');
|
||||||
}
|
}
|
||||||
|
|
||||||
overlay.setupDrag(<HTMLElement>el, {
|
overlay.setupDrag(<HTMLElement>el, {
|
||||||
@ -1051,7 +1053,7 @@ export class DockviewComponent
|
|||||||
case 'right':
|
case 'right':
|
||||||
return this.createGroupAtLocation([this.gridview.length]); // insert into last position
|
return this.createGroupAtLocation([this.gridview.length]); // insert into last position
|
||||||
default:
|
default:
|
||||||
throw new Error(`unsupported position ${position}`);
|
throw new Error(`dockview: unsupported position ${position}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1219,17 +1221,62 @@ export class DockviewComponent
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fromJSON(data: SerializedDockview): void {
|
fromJSON(
|
||||||
|
data: SerializedDockview,
|
||||||
|
options?: { reuseExistingPanels: boolean }
|
||||||
|
): void {
|
||||||
|
const existingPanels = new Map<string, IDockviewPanel>();
|
||||||
|
|
||||||
|
let tempGroup: DockviewGroupPanel | undefined;
|
||||||
|
|
||||||
|
if (options?.reuseExistingPanels) {
|
||||||
|
/**
|
||||||
|
* What are we doing here?
|
||||||
|
*
|
||||||
|
* 1. Create a temporary group to hold any panels that currently exist and that also exist in the new layout
|
||||||
|
* 2. Remove that temporary group from the group mapping so that it doesn't get cleared when we clear the layout
|
||||||
|
*/
|
||||||
|
|
||||||
|
tempGroup = this.createGroup();
|
||||||
|
this._groups.delete(tempGroup.api.id);
|
||||||
|
|
||||||
|
const newPanels = Object.keys(data.panels);
|
||||||
|
|
||||||
|
for (const panel of this.panels) {
|
||||||
|
if (newPanels.includes(panel.api.id)) {
|
||||||
|
existingPanels.set(panel.api.id, panel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.runWithSuppressedEvents(() => {
|
||||||
|
Array.from(existingPanels.values()).forEach((panel) => {
|
||||||
|
this.moveGroupOrPanel({
|
||||||
|
from: {
|
||||||
|
groupId: panel.api.group.api.id,
|
||||||
|
panelId: panel.api.id,
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
group: tempGroup!,
|
||||||
|
position: 'center',
|
||||||
|
},
|
||||||
|
keepEmptyGroups: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.clear();
|
this.clear();
|
||||||
|
|
||||||
if (typeof data !== 'object' || data === null) {
|
if (typeof data !== 'object' || data === null) {
|
||||||
throw new Error('serialized layout must be a non-null object');
|
throw new Error(
|
||||||
|
'dockview: serialized layout must be a non-null object'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { grid, panels, activeGroup } = data;
|
const { grid, panels, activeGroup } = data;
|
||||||
|
|
||||||
if (grid.root.type !== 'branch' || !Array.isArray(grid.root.data)) {
|
if (grid.root.type !== 'branch' || !Array.isArray(grid.root.data)) {
|
||||||
throw new Error('root must be of type branch');
|
throw new Error('dockview: root must be of type branch');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1243,7 +1290,9 @@ export class DockviewComponent
|
|||||||
const { id, locked, hideHeader, views, activeView } = data;
|
const { id, locked, hideHeader, views, activeView } = data;
|
||||||
|
|
||||||
if (typeof id !== 'string') {
|
if (typeof id !== 'string') {
|
||||||
throw new Error('group id must be of type string');
|
throw new Error(
|
||||||
|
'dockview: group id must be of type string'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const group = this.createGroup({
|
const group = this.createGroup({
|
||||||
@ -1260,11 +1309,23 @@ export class DockviewComponent
|
|||||||
* In running this section first we avoid firing lots of 'add' events in the event of a failure
|
* In running this section first we avoid firing lots of 'add' events in the event of a failure
|
||||||
* due to a corruption of input data.
|
* due to a corruption of input data.
|
||||||
*/
|
*/
|
||||||
const panel = this._deserializer.fromJSON(
|
|
||||||
panels[child],
|
const existingPanel = existingPanels.get(child);
|
||||||
group
|
|
||||||
);
|
if (tempGroup && existingPanel) {
|
||||||
createdPanels.push(panel);
|
this.runWithSuppressedEvents(() => {
|
||||||
|
tempGroup!.model.removePanel(existingPanel);
|
||||||
|
});
|
||||||
|
|
||||||
|
createdPanels.push(existingPanel);
|
||||||
|
existingPanel.updateFromStateModel(panels[child]);
|
||||||
|
} else {
|
||||||
|
const panel = this._deserializer.fromJSON(
|
||||||
|
panels[child],
|
||||||
|
group
|
||||||
|
);
|
||||||
|
createdPanels.push(panel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._onDidAddGroup.fire(group);
|
this._onDidAddGroup.fire(group);
|
||||||
@ -1276,10 +1337,21 @@ export class DockviewComponent
|
|||||||
typeof activeView === 'string' &&
|
typeof activeView === 'string' &&
|
||||||
activeView === panel.id;
|
activeView === panel.id;
|
||||||
|
|
||||||
group.model.openPanel(panel, {
|
const hasExisting = existingPanels.has(panel.api.id);
|
||||||
skipSetActive: !isActive,
|
|
||||||
skipSetGroupActive: true,
|
if (hasExisting) {
|
||||||
});
|
this.runWithSuppressedEvents(() => {
|
||||||
|
group.model.openPanel(panel, {
|
||||||
|
skipSetActive: !isActive,
|
||||||
|
skipSetGroupActive: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
group.model.openPanel(panel, {
|
||||||
|
skipSetActive: !isActive,
|
||||||
|
skipSetGroupActive: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!group.activePanel && group.panels.length > 0) {
|
if (!group.activePanel && group.panels.length > 0) {
|
||||||
@ -1426,14 +1498,16 @@ export class DockviewComponent
|
|||||||
options: AddPanelOptions<T>
|
options: AddPanelOptions<T>
|
||||||
): DockviewPanel {
|
): DockviewPanel {
|
||||||
if (this.panels.find((_) => _.id === options.id)) {
|
if (this.panels.find((_) => _.id === options.id)) {
|
||||||
throw new Error(`panel with id ${options.id} already exists`);
|
throw new Error(
|
||||||
|
`dockview: panel with id ${options.id} already exists`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let referenceGroup: DockviewGroupPanel | undefined;
|
let referenceGroup: DockviewGroupPanel | undefined;
|
||||||
|
|
||||||
if (options.position && options.floating) {
|
if (options.position && options.floating) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'you can only provide one of: position, floating as arguments to .addPanel(...)'
|
'dockview: you can only provide one of: position, floating as arguments to .addPanel(...)'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1454,7 +1528,7 @@ export class DockviewComponent
|
|||||||
|
|
||||||
if (!referencePanel) {
|
if (!referencePanel) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`referencePanel '${options.position.referencePanel}' does not exist`
|
`dockview: referencePanel '${options.position.referencePanel}' does not exist`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1469,7 +1543,7 @@ export class DockviewComponent
|
|||||||
|
|
||||||
if (!referenceGroup) {
|
if (!referenceGroup) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`referenceGroup '${options.position.referenceGroup}' does not exist`
|
`dockview: referenceGroup '${options.position.referenceGroup}' does not exist`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1634,7 +1708,7 @@ export class DockviewComponent
|
|||||||
|
|
||||||
if (!group) {
|
if (!group) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`cannot remove panel ${panel.id}. it's missing a group.`
|
`dockview: cannot remove panel ${panel.id}. it's missing a group.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1700,7 +1774,7 @@ export class DockviewComponent
|
|||||||
|
|
||||||
if (!referencePanel) {
|
if (!referencePanel) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`reference panel ${options.referencePanel} does not exist`
|
`dockview: reference panel ${options.referencePanel} does not exist`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1708,7 +1782,7 @@ export class DockviewComponent
|
|||||||
|
|
||||||
if (!referenceGroup) {
|
if (!referenceGroup) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`reference group for reference panel ${options.referencePanel} does not exist`
|
`dockview: reference group for reference panel ${options.referencePanel} does not exist`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (isGroupOptionsWithGroup(options)) {
|
} else if (isGroupOptionsWithGroup(options)) {
|
||||||
@ -1719,7 +1793,7 @@ export class DockviewComponent
|
|||||||
|
|
||||||
if (!referenceGroup) {
|
if (!referenceGroup) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`reference group ${options.referenceGroup} does not exist`
|
`dockview: reference group ${options.referenceGroup} does not exist`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1832,7 +1906,7 @@ export class DockviewComponent
|
|||||||
return floatingGroup.group;
|
return floatingGroup.group;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('failed to find floating group');
|
throw new Error('dockview: failed to find floating group');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group.api.location.type === 'popout') {
|
if (group.api.location.type === 'popout') {
|
||||||
@ -1878,7 +1952,7 @@ export class DockviewComponent
|
|||||||
return selectedGroup.popoutGroup;
|
return selectedGroup.popoutGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('failed to find popout group');
|
throw new Error('dockview: failed to find popout group');
|
||||||
}
|
}
|
||||||
|
|
||||||
const re = super.doRemoveGroup(group, options);
|
const re = super.doRemoveGroup(group, options);
|
||||||
@ -1892,16 +1966,21 @@ export class DockviewComponent
|
|||||||
return re;
|
return re;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _moving = false;
|
private _isEventSuppressionEnabled = false;
|
||||||
|
|
||||||
movingLock<T>(func: () => T): T {
|
/**
|
||||||
const isMoving = this._moving;
|
* Code that runs within the provided function will not cause any events to fire. This is useful if you want
|
||||||
|
* to move things around as an intermediate step without raises any associated events
|
||||||
|
*/
|
||||||
|
runWithSuppressedEvents<T>(func: () => T): T {
|
||||||
|
const isMoving = this._isEventSuppressionEnabled;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this._moving = true;
|
this._isEventSuppressionEnabled = true;
|
||||||
return func();
|
return func();
|
||||||
} finally {
|
} finally {
|
||||||
this._moving = isMoving;
|
// return to the original state which isn't necessarily false since calls may be nested
|
||||||
|
this._isEventSuppressionEnabled = isMoving;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1917,7 +1996,9 @@ export class DockviewComponent
|
|||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (!sourceGroup) {
|
if (!sourceGroup) {
|
||||||
throw new Error(`Failed to find group id ${sourceGroupId}`);
|
throw new Error(
|
||||||
|
`dockview: Failed to find group id ${sourceGroupId}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sourceItemId === undefined) {
|
if (sourceItemId === undefined) {
|
||||||
@ -1940,24 +2021,24 @@ export class DockviewComponent
|
|||||||
* Dropping a panel within another group
|
* Dropping a panel within another group
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const removedPanel: IDockviewPanel | undefined = this.movingLock(
|
const removedPanel: IDockviewPanel | undefined =
|
||||||
() =>
|
this.runWithSuppressedEvents(() =>
|
||||||
sourceGroup.model.removePanel(sourceItemId, {
|
sourceGroup.model.removePanel(sourceItemId, {
|
||||||
skipSetActive: false,
|
skipSetActive: false,
|
||||||
skipSetActiveGroup: true,
|
skipSetActiveGroup: true,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!removedPanel) {
|
if (!removedPanel) {
|
||||||
throw new Error(`No panel with id ${sourceItemId}`);
|
throw new Error(`dockview: No panel with id ${sourceItemId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sourceGroup.model.size === 0) {
|
if (!options.keepEmptyGroups && sourceGroup.model.size === 0) {
|
||||||
// remove the group and do not set a new group as active
|
// remove the group and do not set a new group as active
|
||||||
this.doRemoveGroup(sourceGroup, { skipActive: true });
|
this.doRemoveGroup(sourceGroup, { skipActive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.movingLock(() =>
|
this.runWithSuppressedEvents(() =>
|
||||||
destinationGroup.model.openPanel(removedPanel, {
|
destinationGroup.model.openPanel(removedPanel, {
|
||||||
index: destinationIndex,
|
index: destinationIndex,
|
||||||
skipSetGroupActive: true,
|
skipSetGroupActive: true,
|
||||||
@ -2028,7 +2109,7 @@ export class DockviewComponent
|
|||||||
)!;
|
)!;
|
||||||
|
|
||||||
const removedPanel: IDockviewPanel | undefined =
|
const removedPanel: IDockviewPanel | undefined =
|
||||||
this.movingLock(() =>
|
this.runWithSuppressedEvents(() =>
|
||||||
popoutGroup.popoutGroup.model.removePanel(
|
popoutGroup.popoutGroup.model.removePanel(
|
||||||
popoutGroup.popoutGroup.panels[0],
|
popoutGroup.popoutGroup.panels[0],
|
||||||
{
|
{
|
||||||
@ -2041,7 +2122,7 @@ export class DockviewComponent
|
|||||||
this.doRemoveGroup(sourceGroup, { skipActive: true });
|
this.doRemoveGroup(sourceGroup, { skipActive: true });
|
||||||
|
|
||||||
const newGroup = this.createGroupAtLocation(targetLocation);
|
const newGroup = this.createGroupAtLocation(targetLocation);
|
||||||
this.movingLock(() =>
|
this.runWithSuppressedEvents(() =>
|
||||||
newGroup.model.openPanel(removedPanel, {
|
newGroup.model.openPanel(removedPanel, {
|
||||||
skipSetActive: true,
|
skipSetActive: true,
|
||||||
})
|
})
|
||||||
@ -2056,7 +2137,7 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
// source group will become empty so delete the group
|
// source group will become empty so delete the group
|
||||||
const targetGroup = this.movingLock(() =>
|
const targetGroup = this.runWithSuppressedEvents(() =>
|
||||||
this.doRemoveGroup(sourceGroup, {
|
this.doRemoveGroup(sourceGroup, {
|
||||||
skipActive: true,
|
skipActive: true,
|
||||||
skipDispose: true,
|
skipDispose: true,
|
||||||
@ -2073,7 +2154,9 @@ export class DockviewComponent
|
|||||||
updatedReferenceLocation,
|
updatedReferenceLocation,
|
||||||
destinationTarget
|
destinationTarget
|
||||||
);
|
);
|
||||||
this.movingLock(() => this.doAddGroup(targetGroup, location));
|
this.runWithSuppressedEvents(() =>
|
||||||
|
this.doAddGroup(targetGroup, location)
|
||||||
|
);
|
||||||
this.doSetGroupAndPanelActive(targetGroup);
|
this.doSetGroupAndPanelActive(targetGroup);
|
||||||
|
|
||||||
this._onDidMovePanel.fire({
|
this._onDidMovePanel.fire({
|
||||||
@ -2086,7 +2169,7 @@ export class DockviewComponent
|
|||||||
* create a new group, add the panels to that new group and add the new group in an appropiate position
|
* create a new group, add the panels to that new group and add the new group in an appropiate position
|
||||||
*/
|
*/
|
||||||
const removedPanel: IDockviewPanel | undefined =
|
const removedPanel: IDockviewPanel | undefined =
|
||||||
this.movingLock(() =>
|
this.runWithSuppressedEvents(() =>
|
||||||
sourceGroup.model.removePanel(sourceItemId, {
|
sourceGroup.model.removePanel(sourceItemId, {
|
||||||
skipSetActive: false,
|
skipSetActive: false,
|
||||||
skipSetActiveGroup: true,
|
skipSetActiveGroup: true,
|
||||||
@ -2094,7 +2177,9 @@ export class DockviewComponent
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!removedPanel) {
|
if (!removedPanel) {
|
||||||
throw new Error(`No panel with id ${sourceItemId}`);
|
throw new Error(
|
||||||
|
`dockview: No panel with id ${sourceItemId}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dropLocation = getRelativeLocation(
|
const dropLocation = getRelativeLocation(
|
||||||
@ -2104,7 +2189,7 @@ export class DockviewComponent
|
|||||||
);
|
);
|
||||||
|
|
||||||
const group = this.createGroupAtLocation(dropLocation);
|
const group = this.createGroupAtLocation(dropLocation);
|
||||||
this.movingLock(() =>
|
this.runWithSuppressedEvents(() =>
|
||||||
group.model.openPanel(removedPanel, {
|
group.model.openPanel(removedPanel, {
|
||||||
skipSetGroupActive: true,
|
skipSetGroupActive: true,
|
||||||
})
|
})
|
||||||
@ -2127,7 +2212,7 @@ export class DockviewComponent
|
|||||||
if (target === 'center') {
|
if (target === 'center') {
|
||||||
const activePanel = from.activePanel;
|
const activePanel = from.activePanel;
|
||||||
|
|
||||||
const panels = this.movingLock(() =>
|
const panels = this.runWithSuppressedEvents(() =>
|
||||||
[...from.panels].map((p) =>
|
[...from.panels].map((p) =>
|
||||||
from.model.removePanel(p.id, {
|
from.model.removePanel(p.id, {
|
||||||
skipSetActive: true,
|
skipSetActive: true,
|
||||||
@ -2139,7 +2224,7 @@ export class DockviewComponent
|
|||||||
this.doRemoveGroup(from, { skipActive: true });
|
this.doRemoveGroup(from, { skipActive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.movingLock(() => {
|
this.runWithSuppressedEvents(() => {
|
||||||
for (const panel of panels) {
|
for (const panel of panels) {
|
||||||
to.model.openPanel(panel, {
|
to.model.openPanel(panel, {
|
||||||
skipSetActive: panel !== activePanel,
|
skipSetActive: panel !== activePanel,
|
||||||
@ -2159,7 +2244,9 @@ export class DockviewComponent
|
|||||||
(x) => x.group === from
|
(x) => x.group === from
|
||||||
);
|
);
|
||||||
if (!selectedFloatingGroup) {
|
if (!selectedFloatingGroup) {
|
||||||
throw new Error('failed to find floating group');
|
throw new Error(
|
||||||
|
'dockview: failed to find floating group'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
selectedFloatingGroup.dispose();
|
selectedFloatingGroup.dispose();
|
||||||
break;
|
break;
|
||||||
@ -2169,7 +2256,9 @@ export class DockviewComponent
|
|||||||
(x) => x.popoutGroup === from
|
(x) => x.popoutGroup === from
|
||||||
);
|
);
|
||||||
if (!selectedPopoutGroup) {
|
if (!selectedPopoutGroup) {
|
||||||
throw new Error('failed to find popout group');
|
throw new Error(
|
||||||
|
'dockview: failed to find popout group'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
selectedPopoutGroup.disposable.dispose();
|
selectedPopoutGroup.disposable.dispose();
|
||||||
}
|
}
|
||||||
@ -2213,7 +2302,7 @@ export class DockviewComponent
|
|||||||
const activePanel = this.activePanel;
|
const activePanel = this.activePanel;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!this._moving &&
|
!this._isEventSuppressionEnabled &&
|
||||||
activePanel !== this._onDidActivePanelChange.value
|
activePanel !== this._onDidActivePanelChange.value
|
||||||
) {
|
) {
|
||||||
this._onDidActivePanelChange.fire(activePanel);
|
this._onDidActivePanelChange.fire(activePanel);
|
||||||
@ -2234,7 +2323,7 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!this._moving &&
|
!this._isEventSuppressionEnabled &&
|
||||||
activePanel !== this._onDidActivePanelChange.value
|
activePanel !== this._onDidActivePanelChange.value
|
||||||
) {
|
) {
|
||||||
this._onDidActivePanelChange.fire(activePanel);
|
this._onDidActivePanelChange.fire(activePanel);
|
||||||
@ -2311,19 +2400,19 @@ export class DockviewComponent
|
|||||||
this._onUnhandledDragOverEvent.fire(event);
|
this._onUnhandledDragOverEvent.fire(event);
|
||||||
}),
|
}),
|
||||||
view.model.onDidAddPanel((event) => {
|
view.model.onDidAddPanel((event) => {
|
||||||
if (this._moving) {
|
if (this._isEventSuppressionEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._onDidAddPanel.fire(event.panel);
|
this._onDidAddPanel.fire(event.panel);
|
||||||
}),
|
}),
|
||||||
view.model.onDidRemovePanel((event) => {
|
view.model.onDidRemovePanel((event) => {
|
||||||
if (this._moving) {
|
if (this._isEventSuppressionEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._onDidRemovePanel.fire(event.panel);
|
this._onDidRemovePanel.fire(event.panel);
|
||||||
}),
|
}),
|
||||||
view.model.onDidActivePanelChange((event) => {
|
view.model.onDidActivePanelChange((event) => {
|
||||||
if (this._moving) {
|
if (this._isEventSuppressionEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.panel !== this.activePanel) {
|
if (event.panel !== this.activePanel) {
|
||||||
|
@ -27,6 +27,7 @@ export interface IDockviewPanel extends IDisposable, IPanel {
|
|||||||
group: DockviewGroupPanel,
|
group: DockviewGroupPanel,
|
||||||
options?: { skipSetActive?: boolean }
|
options?: { skipSetActive?: boolean }
|
||||||
): void;
|
): void;
|
||||||
|
updateFromStateModel(state: GroupviewPanelState): void;
|
||||||
init(params: IGroupPanelInitParameters): void;
|
init(params: IGroupPanelInitParameters): void;
|
||||||
toJSON(): GroupviewPanelState;
|
toJSON(): GroupviewPanelState;
|
||||||
setTitle(title: string): void;
|
setTitle(title: string): void;
|
||||||
@ -45,10 +46,10 @@ export class DockviewPanel
|
|||||||
private _title: string | undefined;
|
private _title: string | undefined;
|
||||||
private _renderer: DockviewPanelRenderer | undefined;
|
private _renderer: DockviewPanelRenderer | undefined;
|
||||||
|
|
||||||
private readonly _minimumWidth: number | undefined;
|
private _minimumWidth: number | undefined;
|
||||||
private readonly _minimumHeight: number | undefined;
|
private _minimumHeight: number | undefined;
|
||||||
private readonly _maximumWidth: number | undefined;
|
private _maximumWidth: number | undefined;
|
||||||
private readonly _maximumHeight: number | undefined;
|
private _maximumHeight: number | undefined;
|
||||||
|
|
||||||
get params(): Parameters | undefined {
|
get params(): Parameters | undefined {
|
||||||
return this._params;
|
return this._params;
|
||||||
@ -209,6 +210,20 @@ export class DockviewPanel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateFromStateModel(state: GroupviewPanelState): void {
|
||||||
|
this._maximumHeight = state.maximumHeight;
|
||||||
|
this._minimumHeight = state.minimumHeight;
|
||||||
|
this._maximumWidth = state.maximumWidth;
|
||||||
|
this._minimumWidth = state.minimumWidth;
|
||||||
|
|
||||||
|
this.update({ params: state.params ?? {} });
|
||||||
|
this.setTitle(state.title ?? this.id);
|
||||||
|
this.setRenderer(state.renderer ?? this.accessor.renderer);
|
||||||
|
|
||||||
|
// state.contentComponent;
|
||||||
|
// state.tabComponent;
|
||||||
|
}
|
||||||
|
|
||||||
public updateParentGroup(
|
public updateParentGroup(
|
||||||
group: DockviewGroupPanel,
|
group: DockviewGroupPanel,
|
||||||
options?: { skipSetActive?: boolean }
|
options?: { skipSetActive?: boolean }
|
||||||
|
@ -35,6 +35,12 @@ const components = {
|
|||||||
const isDebug = React.useContext(DebugContext);
|
const isDebug = React.useContext(DebugContext);
|
||||||
const metadata = usePanelApiMetadata(props.api);
|
const metadata = usePanelApiMetadata(props.api);
|
||||||
|
|
||||||
|
const [firstRender, setFirstRender] = React.useState<string>('');
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setFirstRender(new Date().toISOString());
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@ -59,6 +65,8 @@ const components = {
|
|||||||
{props.api.title}
|
{props.api.title}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<div>{firstRender}</div>
|
||||||
|
|
||||||
{isDebug && (
|
{isDebug && (
|
||||||
<div style={{ fontSize: '0.8em' }}>
|
<div style={{ fontSize: '0.8em' }}>
|
||||||
<Option
|
<Option
|
||||||
|
@ -110,6 +110,22 @@ export const GridActions = (props: {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onLoad2 = () => {
|
||||||
|
const state = localStorage.getItem('dv-demo-state');
|
||||||
|
if (state) {
|
||||||
|
try {
|
||||||
|
props.api?.fromJSON(JSON.parse(state), {
|
||||||
|
keepExistingPanels: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
setGap(props.api?.gap ?? 0);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('failed to load state', err);
|
||||||
|
localStorage.removeItem('dv-demo-state');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onSave = () => {
|
const onSave = () => {
|
||||||
if (props.api) {
|
if (props.api) {
|
||||||
console.log(props.api.toJSON());
|
console.log(props.api.toJSON());
|
||||||
@ -192,6 +208,9 @@ export const GridActions = (props: {
|
|||||||
<button className="text-button" onClick={onLoad}>
|
<button className="text-button" onClick={onLoad}>
|
||||||
Load
|
Load
|
||||||
</button>
|
</button>
|
||||||
|
<button className="text-button" onClick={onLoad2}>
|
||||||
|
Load2
|
||||||
|
</button>
|
||||||
<button className="text-button" onClick={onSave}>
|
<button className="text-button" onClick={onSave}>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
|
Loading…
Reference in New Issue
Block a user