mirror of
https://github.com/mathuo/dockview
synced 2025-09-02 23:46:31 +00:00
Merge pull request #87 from mathuo/84-add-ability-to-have-fixed-panel-with-no-tab-in-dockviewreact
84 add ability to have fixed panel with no tab in dockviewreact
This commit is contained in:
commit
d7d6dc2635
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,3 +11,4 @@ storybook-static/
|
|||||||
.rollup.cache/
|
.rollup.cache/
|
||||||
test-report.xml
|
test-report.xml
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
|
yarn-error.log
|
||||||
|
@ -25,7 +25,8 @@
|
|||||||
"prepack": "npm run rebuild && npm run test",
|
"prepack": "npm run rebuild && npm run test",
|
||||||
"rebuild": "npm run clean && npm run build",
|
"rebuild": "npm run clean && npm run build",
|
||||||
"test": "cross-env ../../node_modules/.bin/jest --selectProjects dockview",
|
"test": "cross-env ../../node_modules/.bin/jest --selectProjects dockview",
|
||||||
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage"
|
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage",
|
||||||
|
"dev-publish": "node ./scripts/publishExperimental.js"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
|
63
packages/dockview/scripts/publishExperimental.js
Normal file
63
packages/dockview/scripts/publishExperimental.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
const cp = require('child_process');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const rootDir = path.join(__dirname, '..');
|
||||||
|
const publishDir = path.join(rootDir, '__publish__');
|
||||||
|
|
||||||
|
cp.execSync('npm run clean', { cwd: rootDir, stdio: 'inherit' });
|
||||||
|
cp.execSync('npm run test', { cwd: __dirname, stdio: 'inherit' });
|
||||||
|
cp.execSync('npm run build', { cwd: rootDir, stdio: 'inherit' });
|
||||||
|
|
||||||
|
if (fs.existsSync(publishDir)) {
|
||||||
|
fs.removeSync(publishDir);
|
||||||
|
}
|
||||||
|
fs.mkdirSync(publishDir);
|
||||||
|
|
||||||
|
if (!fs.existsSync(path.join(publishDir, 'dist'))) {
|
||||||
|
fs.mkdirSync(path.join(publishDir, 'dist'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const package = JSON.parse(
|
||||||
|
fs.readFileSync(path.join(rootDir, 'package.json')).toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const file of package.files) {
|
||||||
|
fs.copySync(path.join(rootDir, file), path.join(publishDir, file));
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = cp
|
||||||
|
.execSync('git rev-parse --short HEAD', {
|
||||||
|
cwd: rootDir,
|
||||||
|
})
|
||||||
|
.toString()
|
||||||
|
.replace(/\n/g, '');
|
||||||
|
|
||||||
|
function formatDate() {
|
||||||
|
const date = new Date();
|
||||||
|
|
||||||
|
function pad(value) {
|
||||||
|
if (value.toString().length === 1) {
|
||||||
|
return `0${value}`;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(
|
||||||
|
date.getDate()
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
package.version = `0.0.0-experimental-${result}-${formatDate()}`;
|
||||||
|
package.scripts = {};
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(publishDir, 'package.json'),
|
||||||
|
JSON.stringify(package, null, 4)
|
||||||
|
);
|
||||||
|
|
||||||
|
const command = 'npm publish --tag experimental';
|
||||||
|
|
||||||
|
cp.execSync(command, { cwd: publishDir, stdio: 'inherit' });
|
||||||
|
|
||||||
|
fs.removeSync(publishDir);
|
@ -20,6 +20,11 @@ describe('droptarget', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
element = document.createElement('div');
|
element = document.createElement('div');
|
||||||
|
|
||||||
|
jest.spyOn(element, 'clientHeight', 'get').mockImplementation(
|
||||||
|
() => 100
|
||||||
|
);
|
||||||
|
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('non-directional', () => {
|
test('non-directional', () => {
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
import {
|
||||||
|
DefaultDeserializer,
|
||||||
|
PanelDeserializerOptions,
|
||||||
|
} from '../../dockview/deserializer';
|
||||||
|
import { DockviewComponent } from '../../dockview/dockviewComponent';
|
||||||
|
import { Groupview } from '../../groupview/groupview';
|
||||||
|
import { GroupviewPanel } from '../../groupview/groupviewPanel';
|
||||||
|
|
||||||
|
describe('deserializer', () => {
|
||||||
|
test('fromJSON', () => {
|
||||||
|
const openPanel = jest.fn();
|
||||||
|
|
||||||
|
const model = jest.fn<Groupview, []>(() => {
|
||||||
|
const result: Partial<Groupview> = {
|
||||||
|
openPanel,
|
||||||
|
};
|
||||||
|
|
||||||
|
return result as Groupview;
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel1 = jest.fn();
|
||||||
|
const panel2 = jest.fn();
|
||||||
|
|
||||||
|
const groupMock = jest.fn<GroupviewPanel, []>(() => {
|
||||||
|
const result: Partial<GroupviewPanel> = {
|
||||||
|
model: new model(),
|
||||||
|
panels: <any>[panel1, panel2],
|
||||||
|
activePanel: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
return result as GroupviewPanel;
|
||||||
|
});
|
||||||
|
const group = new groupMock();
|
||||||
|
const createGroup = jest.fn().mockReturnValue(new groupMock());
|
||||||
|
|
||||||
|
const dockviewComponentMock = jest.fn<DockviewComponent, []>(() => {
|
||||||
|
const value: Partial<DockviewComponent> = {
|
||||||
|
createGroup,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <DockviewComponent>value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const createPanel = jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation((child) => ({ id: child }));
|
||||||
|
|
||||||
|
const panelDeserializer: PanelDeserializerOptions = {
|
||||||
|
createPanel,
|
||||||
|
};
|
||||||
|
|
||||||
|
const dockviewComponent = new dockviewComponentMock();
|
||||||
|
|
||||||
|
const cut = new DefaultDeserializer(
|
||||||
|
dockviewComponent,
|
||||||
|
panelDeserializer
|
||||||
|
);
|
||||||
|
|
||||||
|
cut.fromJSON({
|
||||||
|
type: 'leaf',
|
||||||
|
size: 100,
|
||||||
|
visible: true,
|
||||||
|
data: {
|
||||||
|
hideHeader: true,
|
||||||
|
locked: true,
|
||||||
|
id: 'id',
|
||||||
|
views: ['view1', 'view2'],
|
||||||
|
activeView: 'view2',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createGroup).toBeCalledWith({
|
||||||
|
id: 'id',
|
||||||
|
locked: true,
|
||||||
|
hideHeader: true,
|
||||||
|
});
|
||||||
|
expect(createGroup).toBeCalledTimes(1);
|
||||||
|
|
||||||
|
expect(createPanel).toBeCalledWith('view1', group);
|
||||||
|
expect(createPanel).toBeCalledWith('view2', group);
|
||||||
|
expect(createPanel).toBeCalledTimes(2);
|
||||||
|
|
||||||
|
expect(openPanel).toBeCalledWith(
|
||||||
|
{ id: 'view1' },
|
||||||
|
{ skipSetActive: true }
|
||||||
|
);
|
||||||
|
expect(openPanel).toBeCalledWith(
|
||||||
|
{ id: 'view2' },
|
||||||
|
{ skipSetActive: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(openPanel).toBeCalledWith(panel2);
|
||||||
|
expect(openPanel).toBeCalledTimes(3);
|
||||||
|
});
|
||||||
|
});
|
@ -619,7 +619,7 @@ describe('dockviewComponent', () => {
|
|||||||
data: {
|
data: {
|
||||||
views: ['panel2', 'panel3'],
|
views: ['panel2', 'panel3'],
|
||||||
id: 'group-2',
|
id: 'group-2',
|
||||||
activeView: 'panel2',
|
activeView: 'panel3',
|
||||||
},
|
},
|
||||||
size: 500,
|
size: 500,
|
||||||
},
|
},
|
||||||
|
@ -1,19 +1,26 @@
|
|||||||
import { DockviewComponent } from '../..';
|
import { DockviewComponent } from '../../dockview/dockviewComponent';
|
||||||
import { DockviewApi } from '../../api/component.api';
|
import { DockviewApi } from '../../api/component.api';
|
||||||
import { IGroupPanelView } from '../../dockview/defaultGroupPanelView';
|
import { IGroupPanelView } from '../../dockview/defaultGroupPanelView';
|
||||||
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
||||||
|
import { GroupviewPanel } from '../../groupview/groupviewPanel';
|
||||||
|
|
||||||
describe('dockviewGroupPanel', () => {
|
describe('dockviewGroupPanel', () => {
|
||||||
test('update title', () => {
|
test('update title', () => {
|
||||||
const dockviewApiMock = jest.fn<DockviewApi, []>(() => {
|
const dockviewApiMock = jest.fn<DockviewApi, []>(() => {
|
||||||
return {} as any;
|
return {
|
||||||
|
onDidActiveChange: jest.fn(),
|
||||||
|
} as any;
|
||||||
});
|
});
|
||||||
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
||||||
return {} as any;
|
return {} as any;
|
||||||
});
|
});
|
||||||
|
const groupMock = jest.fn<GroupviewPanel, []>(() => {
|
||||||
|
return {} as any;
|
||||||
|
});
|
||||||
const api = new dockviewApiMock();
|
const api = new dockviewApiMock();
|
||||||
const accessor = new accessorMock();
|
const accessor = new accessorMock();
|
||||||
const cut = new DockviewGroupPanel('fake-id', accessor, api);
|
const group = new groupMock();
|
||||||
|
const cut = new DockviewGroupPanel('fake-id', accessor, api, group);
|
||||||
|
|
||||||
let latestTitle: string | undefined = undefined;
|
let latestTitle: string | undefined = undefined;
|
||||||
|
|
||||||
@ -41,10 +48,14 @@ describe('dockviewGroupPanel', () => {
|
|||||||
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
||||||
return {} as any;
|
return {} as any;
|
||||||
});
|
});
|
||||||
|
const groupMock = jest.fn<GroupviewPanel, []>(() => {
|
||||||
|
return {} as any;
|
||||||
|
});
|
||||||
const api = new dockviewApiMock();
|
const api = new dockviewApiMock();
|
||||||
const accessor = new accessorMock();
|
const accessor = new accessorMock();
|
||||||
|
const group = new groupMock();
|
||||||
|
|
||||||
const cut = new DockviewGroupPanel('fake-id', accessor, api);
|
const cut = new DockviewGroupPanel('fake-id', accessor, api, group);
|
||||||
|
|
||||||
let latestSuppressClosable: boolean | undefined = undefined;
|
let latestSuppressClosable: boolean | undefined = undefined;
|
||||||
|
|
||||||
@ -77,10 +88,14 @@ describe('dockviewGroupPanel', () => {
|
|||||||
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
||||||
return {} as any;
|
return {} as any;
|
||||||
});
|
});
|
||||||
|
const groupMock = jest.fn<GroupviewPanel, []>(() => {
|
||||||
|
return {} as any;
|
||||||
|
});
|
||||||
const api = new dockviewApiMock();
|
const api = new dockviewApiMock();
|
||||||
const accessor = new accessorMock();
|
const accessor = new accessorMock();
|
||||||
|
const group = new groupMock();
|
||||||
|
|
||||||
const cut = new DockviewGroupPanel('fake-id', accessor, api);
|
const cut = new DockviewGroupPanel('fake-id', accessor, api, group);
|
||||||
|
|
||||||
const viewMock = jest.fn<IGroupPanelView, []>(() => {
|
const viewMock = jest.fn<IGroupPanelView, []>(() => {
|
||||||
return {
|
return {
|
||||||
|
@ -15,7 +15,11 @@ import {
|
|||||||
} from '../../groupview/types';
|
} from '../../groupview/types';
|
||||||
import { PanelUpdateEvent } from '../../panel/types';
|
import { PanelUpdateEvent } from '../../panel/types';
|
||||||
import { GroupviewPanel } from '../../groupview/groupviewPanel';
|
import { GroupviewPanel } from '../../groupview/groupviewPanel';
|
||||||
import { GroupChangeKind2, GroupOptions } from '../../groupview/groupview';
|
import {
|
||||||
|
GroupChangeKind2,
|
||||||
|
GroupOptions,
|
||||||
|
Groupview,
|
||||||
|
} from '../../groupview/groupview';
|
||||||
import { DockviewPanelApi } from '../../api/groupPanelApi';
|
import { DockviewPanelApi } from '../../api/groupPanelApi';
|
||||||
import {
|
import {
|
||||||
DefaultGroupPanelView,
|
DefaultGroupPanelView,
|
||||||
@ -463,4 +467,59 @@ describe('groupview', () => {
|
|||||||
const panel = cut.addPanel({ id: 'id', component: 'component' });
|
const panel = cut.addPanel({ id: 'id', component: 'component' });
|
||||||
disposable.dispose();
|
disposable.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('toJSON() default', () => {
|
||||||
|
const dockviewComponent = new DockviewComponent(
|
||||||
|
document.createElement('div'),
|
||||||
|
{
|
||||||
|
components: {
|
||||||
|
component: TestContentPart,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const cut = new Groupview(
|
||||||
|
document.createElement('div'),
|
||||||
|
dockviewComponent,
|
||||||
|
'id',
|
||||||
|
{},
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(cut.toJSON()).toEqual({
|
||||||
|
views: [],
|
||||||
|
activeView: undefined,
|
||||||
|
id: 'id',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toJSON() locked and hideHeader', () => {
|
||||||
|
const dockviewComponent = new DockviewComponent(
|
||||||
|
document.createElement('div'),
|
||||||
|
{
|
||||||
|
components: {
|
||||||
|
component: TestContentPart,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const cut = new Groupview(
|
||||||
|
document.createElement('div'),
|
||||||
|
dockviewComponent,
|
||||||
|
'id',
|
||||||
|
{},
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
cut.locked = true;
|
||||||
|
cut.header.hidden = true;
|
||||||
|
|
||||||
|
expect(cut.toJSON()).toEqual({
|
||||||
|
views: [],
|
||||||
|
activeView: undefined,
|
||||||
|
id: 'id',
|
||||||
|
locked: true,
|
||||||
|
hideHeader: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -17,7 +17,7 @@ export interface SuppressClosableEvent {
|
|||||||
* because it belongs to a groupview
|
* because it belongs to a groupview
|
||||||
*/
|
*/
|
||||||
export interface DockviewPanelApi extends Omit<GridviewPanelApi, 'setVisible'> {
|
export interface DockviewPanelApi extends Omit<GridviewPanelApi, 'setVisible'> {
|
||||||
readonly group: GroupviewPanel | undefined;
|
readonly group: GroupviewPanel;
|
||||||
readonly isGroupActive: boolean;
|
readonly isGroupActive: boolean;
|
||||||
readonly title: string;
|
readonly title: string;
|
||||||
readonly suppressClosable: boolean;
|
readonly suppressClosable: boolean;
|
||||||
@ -29,7 +29,7 @@ export class DockviewPanelApiImpl
|
|||||||
extends GridviewPanelApiImpl
|
extends GridviewPanelApiImpl
|
||||||
implements DockviewPanelApi
|
implements DockviewPanelApi
|
||||||
{
|
{
|
||||||
private _group: GroupviewPanel | undefined;
|
private _group: GroupviewPanel;
|
||||||
|
|
||||||
readonly _onDidTitleChange = new Emitter<TitleEvent>();
|
readonly _onDidTitleChange = new Emitter<TitleEvent>();
|
||||||
readonly onDidTitleChange = this._onDidTitleChange.event;
|
readonly onDidTitleChange = this._onDidTitleChange.event;
|
||||||
@ -60,7 +60,7 @@ export class DockviewPanelApiImpl
|
|||||||
return !!this.group?.isActive;
|
return !!this.group?.isActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
set group(value: GroupviewPanel | undefined) {
|
set group(value: GroupviewPanel) {
|
||||||
const isOldGroupActive = this.isGroupActive;
|
const isOldGroupActive = this.isGroupActive;
|
||||||
|
|
||||||
this._group = value;
|
this._group = value;
|
||||||
@ -78,13 +78,13 @@ export class DockviewPanelApiImpl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get group(): GroupviewPanel | undefined {
|
get group(): GroupviewPanel {
|
||||||
return this._group;
|
return this._group;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private panel: IGroupPanel, group: GroupviewPanel | undefined) {
|
constructor(private panel: IGroupPanel, group: GroupviewPanel) {
|
||||||
super(panel.id);
|
super(panel.id);
|
||||||
this.group = group;
|
this._group = group;
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this.disposable,
|
this.disposable,
|
||||||
|
@ -26,7 +26,9 @@ function isBooleanValue(
|
|||||||
return typeof canDisplayOverlay === 'boolean';
|
return typeof canDisplayOverlay === 'boolean';
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CanDisplayOverlay = boolean | ((dragEvent: DragEvent) => boolean);
|
export type CanDisplayOverlay =
|
||||||
|
| boolean
|
||||||
|
| ((dragEvent: DragEvent, state: Quadrant | null) => boolean);
|
||||||
|
|
||||||
export class Droptarget extends CompositeDisposable {
|
export class Droptarget extends CompositeDisposable {
|
||||||
private target: HTMLElement | undefined;
|
private target: HTMLElement | undefined;
|
||||||
@ -62,11 +64,29 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
new DragAndDropObserver(this.element, {
|
new DragAndDropObserver(this.element, {
|
||||||
onDragEnter: () => undefined,
|
onDragEnter: () => undefined,
|
||||||
onDragOver: (e) => {
|
onDragOver: (e) => {
|
||||||
|
const width = this.element.clientWidth;
|
||||||
|
const height = this.element.clientHeight;
|
||||||
|
|
||||||
|
if (width === 0 || height === 0) {
|
||||||
|
return; // avoid div!0
|
||||||
|
}
|
||||||
|
|
||||||
|
const x = e.offsetX;
|
||||||
|
const y = e.offsetY;
|
||||||
|
const xp = (100 * x) / width;
|
||||||
|
const yp = (100 * y) / height;
|
||||||
|
|
||||||
|
const quadrant = this.calculateQuadrant(
|
||||||
|
this.options.validOverlays,
|
||||||
|
xp,
|
||||||
|
yp
|
||||||
|
);
|
||||||
|
|
||||||
if (isBooleanValue(this.options.canDisplayOverlay)) {
|
if (isBooleanValue(this.options.canDisplayOverlay)) {
|
||||||
if (!this.options.canDisplayOverlay) {
|
if (!this.options.canDisplayOverlay) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (!this.options.canDisplayOverlay(e)) {
|
} else if (!this.options.canDisplayOverlay(e, quadrant)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,24 +110,6 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const width = this.target.clientWidth;
|
|
||||||
const height = this.target.clientHeight;
|
|
||||||
|
|
||||||
if (width === 0 || height === 0) {
|
|
||||||
return; // avoid div!0
|
|
||||||
}
|
|
||||||
|
|
||||||
const x = e.offsetX;
|
|
||||||
const y = e.offsetY;
|
|
||||||
const xp = (100 * x) / width;
|
|
||||||
const yp = (100 * y) / height;
|
|
||||||
|
|
||||||
const quadrant = this.calculateQuadrant(
|
|
||||||
this.options.validOverlays,
|
|
||||||
xp,
|
|
||||||
yp
|
|
||||||
);
|
|
||||||
|
|
||||||
const isSmallX = width < 100;
|
const isSmallX = width < 100;
|
||||||
const isSmallY = height < 100;
|
const isSmallY = height < 100;
|
||||||
|
|
||||||
|
@ -4,36 +4,52 @@ import {
|
|||||||
IViewDeserializer,
|
IViewDeserializer,
|
||||||
} from '../gridview/gridview';
|
} from '../gridview/gridview';
|
||||||
import { GroupviewPanelState, IGroupPanel } from '../groupview/groupPanel';
|
import { GroupviewPanelState, IGroupPanel } from '../groupview/groupPanel';
|
||||||
|
import { GroupPanelViewState } from '../groupview/groupview';
|
||||||
|
import { GroupviewPanel } from '../groupview/groupviewPanel';
|
||||||
import { DockviewComponent } from './dockviewComponent';
|
import { DockviewComponent } from './dockviewComponent';
|
||||||
|
|
||||||
export interface IPanelDeserializer {
|
export interface IPanelDeserializer {
|
||||||
fromJSON(panelData: GroupviewPanelState): IGroupPanel;
|
fromJSON(
|
||||||
|
panelData: GroupviewPanelState,
|
||||||
|
group: GroupviewPanel
|
||||||
|
): IGroupPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PanelDeserializerOptions {
|
||||||
|
createPanel: (id: string, group: GroupviewPanel) => IGroupPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DefaultDeserializer implements IViewDeserializer {
|
export class DefaultDeserializer implements IViewDeserializer {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly layout: DockviewComponent,
|
private readonly layout: DockviewComponent,
|
||||||
private panelDeserializer: {
|
private panelDeserializer: PanelDeserializerOptions
|
||||||
createPanel: (id: string) => IGroupPanel;
|
|
||||||
}
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public fromJSON(node: ISerializedLeafNode): IGridView {
|
public fromJSON(node: ISerializedLeafNode<GroupPanelViewState>): IGridView {
|
||||||
const children = node.data.views;
|
const data = node.data;
|
||||||
const active = node.data.activeView;
|
const children = data.views;
|
||||||
|
const active = data.activeView;
|
||||||
|
|
||||||
const panels: IGroupPanel[] = [];
|
const group = this.layout.createGroup({
|
||||||
|
id: data.id,
|
||||||
|
locked: !!data.locked,
|
||||||
|
hideHeader: !!data.hideHeader,
|
||||||
|
});
|
||||||
|
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
const panel = this.panelDeserializer.createPanel(child);
|
const panel = this.panelDeserializer.createPanel(child, group);
|
||||||
|
|
||||||
panels.push(panel);
|
const isActive = typeof active === 'string' && active === panel.id;
|
||||||
|
|
||||||
|
group.model.openPanel(panel, {
|
||||||
|
skipSetActive: !isActive,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.layout.createGroup({
|
if (!group.activePanel && group.panels.length > 0) {
|
||||||
panels,
|
group.model.openPanel(group.panels[group.panels.length - 1]);
|
||||||
activePanel: panels.find((p) => p.id === active),
|
}
|
||||||
id: node.data.id,
|
|
||||||
});
|
return group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ export interface SerializedDockview {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type DockviewComponentUpdateOptions = Pick<
|
export type DockviewComponentUpdateOptions = Pick<
|
||||||
DockviewComponentOptions,
|
DockviewComponentOptions,
|
||||||
| 'orientation'
|
| 'orientation'
|
||||||
| 'components'
|
| 'components'
|
||||||
| 'frameworkComponents'
|
| 'frameworkComponents'
|
||||||
@ -160,7 +160,7 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
get panels(): IGroupPanel[] {
|
get panels(): IGroupPanel[] {
|
||||||
return this.groups.flatMap((group) => group.model.panels);
|
return this.groups.flatMap((group) => group.panels);
|
||||||
}
|
}
|
||||||
|
|
||||||
get deserializer(): IPanelDeserializer | undefined {
|
get deserializer(): IPanelDeserializer | undefined {
|
||||||
@ -182,13 +182,13 @@ export class DockviewComponent
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return activeGroup.model.activePanel;
|
return activeGroup.activePanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
set tabHeight(height: number | undefined) {
|
set tabHeight(height: number | undefined) {
|
||||||
this.options.tabHeight = height;
|
this.options.tabHeight = height;
|
||||||
this._groups.forEach((value) => {
|
this._groups.forEach((value) => {
|
||||||
value.value.model.tabHeight = height;
|
value.value.model.header.height = height;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,9 +263,6 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
setActivePanel(panel: IGroupPanel): void {
|
setActivePanel(panel: IGroupPanel): void {
|
||||||
if (!panel.group) {
|
|
||||||
throw new Error(`Panel ${panel.id} has no associated group`);
|
|
||||||
}
|
|
||||||
this.doSetGroupActive(panel.group);
|
this.doSetGroupActive(panel.group);
|
||||||
panel.group.model.openPanel(panel);
|
panel.group.model.openPanel(panel);
|
||||||
}
|
}
|
||||||
@ -280,9 +277,9 @@ export class DockviewComponent
|
|||||||
|
|
||||||
if (options.includePanel && options.group) {
|
if (options.includePanel && options.group) {
|
||||||
if (
|
if (
|
||||||
options.group.model.activePanel !==
|
options.group.activePanel !==
|
||||||
options.group.model.panels[
|
options.group.panels[
|
||||||
options.group.model.panels.length - 1
|
options.group.panels.length - 1
|
||||||
]
|
]
|
||||||
) {
|
) {
|
||||||
options.group.model.moveToNext({ suppressRoll: true });
|
options.group.model.moveToNext({ suppressRoll: true });
|
||||||
@ -291,7 +288,7 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
const location = getGridLocation(options.group.element);
|
const location = getGridLocation(options.group.element);
|
||||||
const next = this.gridview.next(location)?.view as GroupviewPanel;
|
const next = <GroupviewPanel>this.gridview.next(location)?.view
|
||||||
this.doSetGroupActive(next);
|
this.doSetGroupActive(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,8 +302,8 @@ export class DockviewComponent
|
|||||||
|
|
||||||
if (options.includePanel && options.group) {
|
if (options.includePanel && options.group) {
|
||||||
if (
|
if (
|
||||||
options.group.model.activePanel !==
|
options.group.activePanel !==
|
||||||
options.group.model.panels[0]
|
options.group.panels[0]
|
||||||
) {
|
) {
|
||||||
options.group.model.moveToPrevious({ suppressRoll: true });
|
options.group.model.moveToPrevious({ suppressRoll: true });
|
||||||
return;
|
return;
|
||||||
@ -367,9 +364,9 @@ export class DockviewComponent
|
|||||||
this.gridview.deserialize(
|
this.gridview.deserialize(
|
||||||
grid,
|
grid,
|
||||||
new DefaultDeserializer(this, {
|
new DefaultDeserializer(this, {
|
||||||
createPanel: (id) => {
|
createPanel: (id, group) => {
|
||||||
const panelData = panels[id];
|
const panelData = panels[id];
|
||||||
return this.deserializer!.fromJSON(panelData);
|
return this.deserializer!.fromJSON(panelData, group);
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -411,8 +408,6 @@ export class DockviewComponent
|
|||||||
throw new Error(`panel with id ${options.id} already exists`);
|
throw new Error(`panel with id ${options.id} already exists`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const panel = this.createPanel(options);
|
|
||||||
|
|
||||||
let referenceGroup: GroupviewPanel | undefined;
|
let referenceGroup: GroupviewPanel | undefined;
|
||||||
|
|
||||||
if (options.position?.referencePanel) {
|
if (options.position?.referencePanel) {
|
||||||
@ -431,9 +426,12 @@ export class DockviewComponent
|
|||||||
referenceGroup = this.activeGroup;
|
referenceGroup = this.activeGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let panel: IGroupPanel
|
||||||
|
|
||||||
if (referenceGroup) {
|
if (referenceGroup) {
|
||||||
const target = toTarget(options.position?.direction || 'within');
|
const target = toTarget(options.position?.direction || 'within');
|
||||||
if (target === Position.Center) {
|
if (target === Position.Center) {
|
||||||
|
panel = this.createPanel(options, referenceGroup)
|
||||||
referenceGroup.model.openPanel(panel);
|
referenceGroup.model.openPanel(panel);
|
||||||
} else {
|
} else {
|
||||||
const location = getGridLocation(referenceGroup.element);
|
const location = getGridLocation(referenceGroup.element);
|
||||||
@ -442,10 +440,14 @@ export class DockviewComponent
|
|||||||
location,
|
location,
|
||||||
target
|
target
|
||||||
);
|
);
|
||||||
this.addPanelToNewGroup(panel, relativeLocation);
|
const group = this.createGroupAtLocation(relativeLocation);
|
||||||
|
panel = this.createPanel(options, group)
|
||||||
|
group.model.openPanel(panel);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.addPanelToNewGroup(panel);
|
const group = this.createGroupAtLocation();
|
||||||
|
panel = this.createPanel(options, group);
|
||||||
|
group.model.openPanel(panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
@ -474,7 +476,7 @@ export class DockviewComponent
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
!retainGroupForWatermark &&
|
!retainGroupForWatermark &&
|
||||||
group.model.size === 0 &&
|
group.size === 0 &&
|
||||||
options.removeEmptyGroup
|
options.removeEmptyGroup
|
||||||
) {
|
) {
|
||||||
this.removeGroup(group);
|
this.removeGroup(group);
|
||||||
@ -532,7 +534,7 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeGroup(group: GroupviewPanel, skipActive = false): void {
|
removeGroup(group: GroupviewPanel, skipActive = false): void {
|
||||||
const panels = [...group.model.panels]; // reassign since group panels will mutate
|
const panels = [...group.panels]; // reassign since group panels will mutate
|
||||||
|
|
||||||
for (const panel of panels) {
|
for (const panel of panels) {
|
||||||
this.removePanel(panel, {
|
this.removePanel(panel, {
|
||||||
@ -577,7 +579,7 @@ export class DockviewComponent
|
|||||||
target
|
target
|
||||||
);
|
);
|
||||||
|
|
||||||
if (sourceGroup && sourceGroup.model.size < 2) {
|
if (sourceGroup && sourceGroup.size < 2) {
|
||||||
const [targetParentLocation, to] = tail(targetLocation);
|
const [targetParentLocation, to] = tail(targetLocation);
|
||||||
const sourceLocation = getGridLocation(sourceGroup.element);
|
const sourceLocation = getGridLocation(sourceGroup.element);
|
||||||
const [sourceParentLocation, from] = tail(sourceLocation);
|
const [sourceParentLocation, from] = tail(sourceLocation);
|
||||||
@ -622,7 +624,8 @@ export class DockviewComponent
|
|||||||
target
|
target
|
||||||
);
|
);
|
||||||
|
|
||||||
this.addPanelToNewGroup(groupItem, dropLocation);
|
const group = this.createGroupAtLocation( dropLocation);
|
||||||
|
group.model.openPanel(groupItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -634,9 +637,9 @@ export class DockviewComponent
|
|||||||
const isGroupAlreadyFocused = this._activeGroup === group;
|
const isGroupAlreadyFocused = this._activeGroup === group;
|
||||||
super.doSetGroupActive(group, skipFocus);
|
super.doSetGroupActive(group, skipFocus);
|
||||||
|
|
||||||
if (!isGroupAlreadyFocused && this._activeGroup?.model.activePanel) {
|
if (!isGroupAlreadyFocused && this._activeGroup?.activePanel) {
|
||||||
this._onDidActivePanelChange.fire(
|
this._onDidActivePanelChange.fire(
|
||||||
this._activeGroup?.model.activePanel
|
this._activeGroup?.activePanel
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -704,19 +707,19 @@ export class DockviewComponent
|
|||||||
view.initialize();
|
view.initialize();
|
||||||
|
|
||||||
if (typeof this.options.tabHeight === 'number') {
|
if (typeof this.options.tabHeight === 'number') {
|
||||||
view.model.tabHeight = this.options.tabHeight;
|
view.model.header.height = this.options.tabHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createPanel(options: AddPanelOptions): IGroupPanel {
|
private createPanel(options: AddPanelOptions, group: GroupviewPanel): IGroupPanel {
|
||||||
const view = new DefaultGroupPanelView({
|
const view = new DefaultGroupPanelView({
|
||||||
content: this.createContentComponent(options.id, options.component),
|
content: this.createContentComponent(options.id, options.component),
|
||||||
tab: this.createTabComponent(options.id, options.tabComponent),
|
tab: this.createTabComponent(options.id, options.tabComponent),
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel = new DockviewGroupPanel(options.id, this, this._api);
|
const panel = new DockviewGroupPanel(options.id, this, this._api, group);
|
||||||
panel.init({
|
panel.init({
|
||||||
view,
|
view,
|
||||||
title: options.title || options.id,
|
title: options.title || options.id,
|
||||||
@ -754,14 +757,12 @@ export class DockviewComponent
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private addPanelToNewGroup(
|
private createGroupAtLocation(
|
||||||
panel: IGroupPanel,
|
|
||||||
location: number[] = [0]
|
location: number[] = [0]
|
||||||
): void {
|
): GroupviewPanel {
|
||||||
const group = this.createGroup();
|
const group = this.createGroup();
|
||||||
this.doAddGroup(group, location);
|
this.doAddGroup(group, location);
|
||||||
|
return group
|
||||||
group.model.openPanel(panel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private findGroup(panel: IGroupPanel): GroupviewPanel | undefined {
|
private findGroup(panel: IGroupPanel): GroupviewPanel | undefined {
|
||||||
|
@ -19,7 +19,7 @@ export class DockviewGroupPanel
|
|||||||
private readonly mutableDisposable = new MutableDisposable();
|
private readonly mutableDisposable = new MutableDisposable();
|
||||||
|
|
||||||
readonly api: DockviewPanelApiImpl;
|
readonly api: DockviewPanelApiImpl;
|
||||||
private _group: GroupviewPanel | undefined;
|
private _group: GroupviewPanel;
|
||||||
private _params?: Parameters;
|
private _params?: Parameters;
|
||||||
|
|
||||||
private _view?: IGroupPanelView;
|
private _view?: IGroupPanelView;
|
||||||
@ -39,7 +39,7 @@ export class DockviewGroupPanel
|
|||||||
return this._suppressClosable;
|
return this._suppressClosable;
|
||||||
}
|
}
|
||||||
|
|
||||||
get group(): GroupviewPanel | undefined {
|
get group(): GroupviewPanel {
|
||||||
return this._group;
|
return this._group;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,11 +50,13 @@ export class DockviewGroupPanel
|
|||||||
constructor(
|
constructor(
|
||||||
public readonly id: string,
|
public readonly id: string,
|
||||||
accessor: DockviewComponent,
|
accessor: DockviewComponent,
|
||||||
private readonly containerApi: DockviewApi
|
private readonly containerApi: DockviewApi,
|
||||||
|
group: GroupviewPanel
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._suppressClosable = false;
|
this._suppressClosable = false;
|
||||||
this._title = '';
|
this._title = '';
|
||||||
|
this._group = group;
|
||||||
|
|
||||||
this.api = new DockviewPanelApiImpl(this, this._group);
|
this.api = new DockviewPanelApiImpl(this, this._group);
|
||||||
|
|
||||||
@ -169,7 +171,7 @@ export class DockviewGroupPanel
|
|||||||
// the obtain the correct dimensions of the content panel we must deduct the tab height
|
// the obtain the correct dimensions of the content panel we must deduct the tab height
|
||||||
this.api._onDidPanelDimensionChange.fire({
|
this.api._onDidPanelDimensionChange.fire({
|
||||||
width,
|
width,
|
||||||
height: height - (this.group?.model.tabHeight || 0),
|
height: height - (this.group.model.header.height || 0),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.view?.layout(width, height);
|
this.view?.layout(width, height);
|
||||||
|
@ -241,9 +241,9 @@ const serializeBranchNode = <T extends IGridView>(
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ISerializedLeafNode {
|
export interface ISerializedLeafNode<T = any> {
|
||||||
type: 'leaf';
|
type: 'leaf';
|
||||||
data: any;
|
data: T;
|
||||||
size: number;
|
size: number;
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ export type GroupPanelUpdateEvent = PanelUpdateEvent<{
|
|||||||
|
|
||||||
export interface IGroupPanel extends IDisposable, IPanel {
|
export interface IGroupPanel extends IDisposable, IPanel {
|
||||||
readonly view?: IGroupPanelView;
|
readonly view?: IGroupPanelView;
|
||||||
readonly group?: GroupviewPanel;
|
readonly group: GroupviewPanel;
|
||||||
readonly api: DockviewPanelApi;
|
readonly api: DockviewPanelApi;
|
||||||
readonly title: string;
|
readonly title: string;
|
||||||
readonly suppressClosable: boolean;
|
readonly suppressClosable: boolean;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { DockviewApi } from '../api/component.api';
|
import { DockviewApi } from '../api/component.api';
|
||||||
import { getPanelData } from '../dnd/dataTransfer';
|
import { getPanelData, PanelTransfer } from '../dnd/dataTransfer';
|
||||||
import { Droptarget, Position } from '../dnd/droptarget';
|
import { Droptarget, Position } from '../dnd/droptarget';
|
||||||
import { IDockviewComponent } from '../dockview/dockviewComponent';
|
import { IDockviewComponent } from '../dockview/dockviewComponent';
|
||||||
import { isAncestor, toggleClass } from '../dom';
|
import { isAncestor, toggleClass } from '../dom';
|
||||||
@ -48,37 +48,52 @@ interface GroupMoveEvent {
|
|||||||
index?: number;
|
index?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupOptions {
|
interface CoreGroupOptions {
|
||||||
|
locked?: boolean;
|
||||||
|
hideHeader?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GroupOptions extends CoreGroupOptions {
|
||||||
readonly panels?: IGroupPanel[];
|
readonly panels?: IGroupPanel[];
|
||||||
readonly activePanel?: IGroupPanel;
|
readonly activePanel?: IGroupPanel;
|
||||||
readonly id?: string;
|
readonly id?: string;
|
||||||
tabHeight?: number;
|
tabHeight?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GroupPanelViewState extends CoreGroupOptions {
|
||||||
|
views: string[];
|
||||||
|
activeView?: string;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GroupviewChangeEvent {
|
export interface GroupviewChangeEvent {
|
||||||
readonly kind: GroupChangeKind2;
|
readonly kind: GroupChangeKind2;
|
||||||
readonly panel?: IGroupPanel;
|
readonly panel?: IGroupPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupPanelViewState {
|
|
||||||
views: string[];
|
|
||||||
activeView?: string;
|
|
||||||
id: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GroupviewDropEvent {
|
export interface GroupviewDropEvent {
|
||||||
nativeEvent: DragEvent;
|
nativeEvent: DragEvent;
|
||||||
position: Position;
|
position: Position;
|
||||||
|
getData(): PanelTransfer | undefined;
|
||||||
index?: number;
|
index?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IHeader {
|
||||||
|
hidden: boolean;
|
||||||
|
height: number | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IGroupview extends IDisposable, IGridPanelView {
|
export interface IGroupview extends IDisposable, IGridPanelView {
|
||||||
readonly isActive: boolean;
|
readonly isActive: boolean;
|
||||||
readonly size: number;
|
readonly size: number;
|
||||||
readonly panels: IGroupPanel[];
|
readonly panels: IGroupPanel[];
|
||||||
readonly tabHeight: number | undefined;
|
|
||||||
readonly activePanel: IGroupPanel | undefined;
|
readonly activePanel: IGroupPanel | undefined;
|
||||||
|
readonly header: IHeader;
|
||||||
|
readonly isContentFocused: boolean;
|
||||||
readonly onDidDrop: Event<GroupviewDropEvent>;
|
readonly onDidDrop: Event<GroupviewDropEvent>;
|
||||||
|
readonly onDidGroupChange: Event<GroupviewChangeEvent>;
|
||||||
|
readonly onMove: Event<GroupMoveEvent>;
|
||||||
|
locked: boolean;
|
||||||
// state
|
// state
|
||||||
isPanelActive: (panel: IGroupPanel) => boolean;
|
isPanelActive: (panel: IGroupPanel) => boolean;
|
||||||
indexOf(panel: IGroupPanel): number;
|
indexOf(panel: IGroupPanel): number;
|
||||||
@ -91,16 +106,11 @@ export interface IGroupview extends IDisposable, IGridPanelView {
|
|||||||
closeAllPanels(): void;
|
closeAllPanels(): void;
|
||||||
containsPanel(panel: IGroupPanel): boolean;
|
containsPanel(panel: IGroupPanel): boolean;
|
||||||
removePanel: (panelOrId: IGroupPanel | string) => IGroupPanel;
|
removePanel: (panelOrId: IGroupPanel | string) => IGroupPanel;
|
||||||
// events
|
|
||||||
onDidGroupChange: Event<GroupviewChangeEvent>;
|
|
||||||
onMove: Event<GroupMoveEvent>;
|
|
||||||
moveToNext(options?: { panel?: IGroupPanel; suppressRoll?: boolean }): void;
|
moveToNext(options?: { panel?: IGroupPanel; suppressRoll?: boolean }): void;
|
||||||
moveToPrevious(options?: {
|
moveToPrevious(options?: {
|
||||||
panel?: IGroupPanel;
|
panel?: IGroupPanel;
|
||||||
suppressRoll?: boolean;
|
suppressRoll?: boolean;
|
||||||
}): void;
|
}): void;
|
||||||
isContentFocused(): boolean;
|
|
||||||
updateActions(): void;
|
|
||||||
canDisplayOverlay(event: DragEvent, target: DockviewDropTargets): boolean;
|
canDisplayOverlay(event: DragEvent, target: DockviewDropTargets): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,6 +121,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
private _activePanel?: IGroupPanel;
|
private _activePanel?: IGroupPanel;
|
||||||
private watermark?: IWatermarkRenderer;
|
private watermark?: IWatermarkRenderer;
|
||||||
private _isGroupActive = false;
|
private _isGroupActive = false;
|
||||||
|
private _locked = false;
|
||||||
|
|
||||||
private mostRecentlyUsed: IGroupPanel[] = [];
|
private mostRecentlyUsed: IGroupPanel[] = [];
|
||||||
|
|
||||||
@ -141,13 +152,12 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
return this._activePanel;
|
return this._activePanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
get tabHeight(): number | undefined {
|
get locked(): boolean {
|
||||||
return this.tabsContainer.height;
|
return this._locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
set tabHeight(height: number | undefined) {
|
set locked(value: boolean) {
|
||||||
this.tabsContainer.height = height;
|
this._locked = value;
|
||||||
this.layout(this._width, this._height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isActive(): boolean {
|
get isActive(): boolean {
|
||||||
@ -188,6 +198,20 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get header(): IHeader {
|
||||||
|
return this.tabsContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isContentFocused(): boolean {
|
||||||
|
if (!document.activeElement) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isAncestor(
|
||||||
|
document.activeElement,
|
||||||
|
this.contentContainer.element
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly container: HTMLElement,
|
private readonly container: HTMLElement,
|
||||||
private accessor: IDockviewComponent,
|
private accessor: IDockviewComponent,
|
||||||
@ -210,9 +234,14 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
tabHeight: options.tabHeight,
|
tabHeight: options.tabHeight,
|
||||||
});
|
});
|
||||||
this.contentContainer = new ContentContainer();
|
this.contentContainer = new ContentContainer();
|
||||||
|
|
||||||
this.dropTarget = new Droptarget(this.contentContainer.element, {
|
this.dropTarget = new Droptarget(this.contentContainer.element, {
|
||||||
validOverlays: 'all',
|
validOverlays: 'all',
|
||||||
canDisplayOverlay: (event) => {
|
canDisplayOverlay: (event, quadrant) => {
|
||||||
|
if (this.locked && !quadrant) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const data = getPanelData();
|
const data = getPanelData();
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
@ -231,6 +260,9 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
this.contentContainer.element
|
this.contentContainer.element
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.header.hidden = !!options.hideHeader;
|
||||||
|
this.locked = !!options.locked;
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this._onMove,
|
this._onMove,
|
||||||
this._onDidGroupChange,
|
this._onDidGroupChange,
|
||||||
@ -266,26 +298,26 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
this.updateContainer();
|
this.updateContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
isContentFocused() {
|
|
||||||
if (!document.activeElement) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return isAncestor(
|
|
||||||
document.activeElement,
|
|
||||||
this.contentContainer.element
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public indexOf(panel: IGroupPanel) {
|
public indexOf(panel: IGroupPanel) {
|
||||||
return this.tabsContainer.indexOf(panel.id);
|
return this.tabsContainer.indexOf(panel.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public toJSON(): GroupPanelViewState {
|
public toJSON(): GroupPanelViewState {
|
||||||
return {
|
const result: GroupPanelViewState = {
|
||||||
views: this.tabsContainer.panels,
|
views: this.tabsContainer.panels,
|
||||||
activeView: this._activePanel?.id,
|
activeView: this._activePanel?.id,
|
||||||
id: this.id,
|
id: this.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.locked) {
|
||||||
|
result.locked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.header.hidden) {
|
||||||
|
result.hideHeader = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public moveToNext(options?: {
|
public moveToNext(options?: {
|
||||||
@ -362,7 +394,11 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
|
|
||||||
public openPanel(
|
public openPanel(
|
||||||
panel: IGroupPanel,
|
panel: IGroupPanel,
|
||||||
options: { index?: number; skipFocus?: boolean } = {}
|
options: {
|
||||||
|
index?: number;
|
||||||
|
skipFocus?: boolean;
|
||||||
|
skipSetActive?: boolean;
|
||||||
|
} = {}
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
typeof options.index !== 'number' ||
|
typeof options.index !== 'number' ||
|
||||||
@ -371,18 +407,22 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
options.index = this.panels.length;
|
options.index = this.panels.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const skipSetActive = !!options.skipSetActive;
|
||||||
|
|
||||||
// ensure the group is updated before we fire any events
|
// ensure the group is updated before we fire any events
|
||||||
panel.updateParentGroup(this.parent, true);
|
panel.updateParentGroup(this.parent, true);
|
||||||
|
|
||||||
if (this._activePanel === panel) {
|
if (!skipSetActive && this._activePanel === panel) {
|
||||||
this.accessor.doSetGroupActive(this.parent);
|
this.accessor.doSetGroupActive(this.parent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.doAddPanel(panel, options.index);
|
this.doAddPanel(panel, options.index);
|
||||||
|
|
||||||
this.doSetActivePanel(panel);
|
if (!skipSetActive) {
|
||||||
this.accessor.doSetGroupActive(this.parent, !!options.skipFocus);
|
this.doSetActivePanel(panel);
|
||||||
|
this.accessor.doSetGroupActive(this.parent, !!options.skipFocus);
|
||||||
|
}
|
||||||
|
|
||||||
this.updateContainer();
|
this.updateContainer();
|
||||||
}
|
}
|
||||||
@ -646,7 +686,12 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
index,
|
index,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this._onDidDrop.fire({ nativeEvent: event, position, index });
|
this._onDidDrop.fire({
|
||||||
|
nativeEvent: event,
|
||||||
|
position,
|
||||||
|
index,
|
||||||
|
getData: () => getPanelData(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,48 +1,86 @@
|
|||||||
import { IFrameworkPart } from '../panel/types';
|
import { IFrameworkPart } from '../panel/types';
|
||||||
import { IDockviewComponent } from '../dockview/dockviewComponent';
|
import { IDockviewComponent } from '../dockview/dockviewComponent';
|
||||||
import { GridviewPanelApiImpl } from '../api/gridviewPanelApi';
|
import {
|
||||||
import { Groupview, GroupOptions } from './groupview';
|
GridviewPanelApi,
|
||||||
|
GridviewPanelApiImpl,
|
||||||
|
} from '../api/gridviewPanelApi';
|
||||||
|
import { Groupview, GroupOptions, IHeader } from './groupview';
|
||||||
import { GridviewPanel, IGridviewPanel } from '../gridview/gridviewPanel';
|
import { GridviewPanel, IGridviewPanel } from '../gridview/gridviewPanel';
|
||||||
|
import { IGroupPanel } from './groupPanel';
|
||||||
|
|
||||||
export interface IGroupviewPanel extends IGridviewPanel {
|
export interface IGroupviewPanel extends IGridviewPanel {
|
||||||
model: Groupview;
|
model: Groupview;
|
||||||
|
locked: boolean;
|
||||||
|
readonly size: number;
|
||||||
|
readonly panels: IGroupPanel[];
|
||||||
|
readonly activePanel: IGroupPanel | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type IGroupviewPanelPublic = IGroupviewPanel;
|
||||||
|
|
||||||
|
export type GroupviewPanelApi = GridviewPanelApi;
|
||||||
|
|
||||||
|
class GroupviewApi extends GridviewPanelApiImpl implements GroupviewPanelApi {}
|
||||||
|
|
||||||
export class GroupviewPanel extends GridviewPanel implements IGroupviewPanel {
|
export class GroupviewPanel extends GridviewPanel implements IGroupviewPanel {
|
||||||
private readonly _model: Groupview;
|
private readonly _model: Groupview;
|
||||||
|
|
||||||
|
get panels(): IGroupPanel[] {
|
||||||
|
return this._model.panels;
|
||||||
|
}
|
||||||
|
|
||||||
|
get activePanel(): IGroupPanel | undefined {
|
||||||
|
return this._model.activePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
get size(): number {
|
||||||
|
return this._model.size;
|
||||||
|
}
|
||||||
|
|
||||||
get model(): Groupview {
|
get model(): Groupview {
|
||||||
return this._model;
|
return this._model;
|
||||||
}
|
}
|
||||||
|
|
||||||
get minimumHeight() {
|
get minimumHeight(): number {
|
||||||
return this._model.minimumHeight;
|
return this._model.minimumHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
get maximumHeight() {
|
get maximumHeight(): number {
|
||||||
return this._model.maximumHeight;
|
return this._model.maximumHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
get minimumWidth() {
|
get minimumWidth(): number {
|
||||||
return this._model.minimumWidth;
|
return this._model.minimumWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
get maximumWidth() {
|
get maximumWidth(): number {
|
||||||
return this._model.maximumWidth;
|
return this._model.maximumWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get locked(): boolean {
|
||||||
|
return this._model.locked;
|
||||||
|
}
|
||||||
|
|
||||||
|
set locked(value: boolean) {
|
||||||
|
this._model.locked = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get header(): IHeader {
|
||||||
|
return this._model.header;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
accessor: IDockviewComponent,
|
accessor: IDockviewComponent,
|
||||||
id: string,
|
id: string,
|
||||||
options: GroupOptions
|
options: GroupOptions
|
||||||
) {
|
) {
|
||||||
super(id, 'groupview_default', new GridviewPanelApiImpl(id));
|
super(id, 'groupview_default', new GroupviewApi(id));
|
||||||
|
|
||||||
this._model = new Groupview(this.element, accessor, id, options, this);
|
this._model = new Groupview(this.element, accessor, id, options, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
this.model.initialize();
|
this._model.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
setActive(isActive: boolean): void {
|
setActive(isActive: boolean): void {
|
||||||
|
@ -33,6 +33,7 @@ export interface ITabsContainer extends IDisposable {
|
|||||||
closePanel: (panel: IGroupPanel) => void;
|
closePanel: (panel: IGroupPanel) => void;
|
||||||
openPanel: (panel: IGroupPanel, index?: number) => void;
|
openPanel: (panel: IGroupPanel, index?: number) => void;
|
||||||
setActionElement(element: HTMLElement | undefined): void;
|
setActionElement(element: HTMLElement | undefined): void;
|
||||||
|
hidden: boolean;
|
||||||
show(): void;
|
show(): void;
|
||||||
hide(): void;
|
hide(): void;
|
||||||
}
|
}
|
||||||
@ -55,6 +56,7 @@ export class TabsContainer
|
|||||||
private actions: HTMLElement | undefined;
|
private actions: HTMLElement | undefined;
|
||||||
|
|
||||||
private _height: number | undefined;
|
private _height: number | undefined;
|
||||||
|
private _hidden = false;
|
||||||
|
|
||||||
private readonly _onDrop = new Emitter<TabDropIndexEvent>();
|
private readonly _onDrop = new Emitter<TabDropIndexEvent>();
|
||||||
readonly onDrop: Event<TabDropIndexEvent> = this._onDrop.event;
|
readonly onDrop: Event<TabDropIndexEvent> = this._onDrop.event;
|
||||||
@ -85,12 +87,23 @@ export class TabsContainer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
get hidden(): boolean {
|
||||||
this.element.style.display = '';
|
return this._hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
set hidden(value: boolean) {
|
||||||
this.element.style.display = 'none';
|
this._hidden = value;
|
||||||
|
this.element.style.display = value ? 'none' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
show(): void {
|
||||||
|
if (!this.hidden) {
|
||||||
|
this.element.style.display = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide(): void {
|
||||||
|
this._element.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
setActionElement(element: HTMLElement | undefined): void {
|
setActionElement(element: HTMLElement | undefined): void {
|
||||||
@ -252,7 +265,7 @@ export class TabsContainer
|
|||||||
tabToAdd.onChanged((event) => {
|
tabToAdd.onChanged((event) => {
|
||||||
const alreadyFocused =
|
const alreadyFocused =
|
||||||
panel.id === this.group.model.activePanel?.id &&
|
panel.id === this.group.model.activePanel?.id &&
|
||||||
this.group.model.isContentFocused();
|
this.group.model.isContentFocused;
|
||||||
this.accessor.fireMouseEvent({ ...event, panel, tab: true });
|
this.accessor.fireMouseEvent({ ...event, panel, tab: true });
|
||||||
|
|
||||||
const isLeftClick = event.event.button === 0;
|
const isLeftClick = event.event.button === 0;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
export * from './dnd/dataTransfer';
|
export * from './dnd/dataTransfer';
|
||||||
export * from './api/component.api';
|
|
||||||
|
|
||||||
export * from './splitview/core/splitview';
|
export * from './splitview/core/splitview';
|
||||||
export * from './paneview/paneview';
|
export * from './paneview/paneview';
|
||||||
@ -50,3 +49,10 @@ export {
|
|||||||
SplitviewPanelApi,
|
SplitviewPanelApi,
|
||||||
} from './api/splitviewPanelApi';
|
} from './api/splitviewPanelApi';
|
||||||
export { ExpansionEvent, PaneviewPanelApi } from './api/paneviewPanelApi';
|
export { ExpansionEvent, PaneviewPanelApi } from './api/paneviewPanelApi';
|
||||||
|
export {
|
||||||
|
CommonApi,
|
||||||
|
SplitviewApi,
|
||||||
|
PaneviewApi,
|
||||||
|
GridviewApi,
|
||||||
|
DockviewApi,
|
||||||
|
} from './api/component.api';
|
||||||
|
@ -6,11 +6,15 @@ import { createComponent } from '../panel/componentFactory';
|
|||||||
import { DockviewApi } from '../api/component.api';
|
import { DockviewApi } from '../api/component.api';
|
||||||
import { DefaultTab } from '../dockview/components/tab/defaultTab';
|
import { DefaultTab } from '../dockview/components/tab/defaultTab';
|
||||||
import { DefaultGroupPanelView } from '../dockview/defaultGroupPanelView';
|
import { DefaultGroupPanelView } from '../dockview/defaultGroupPanelView';
|
||||||
|
import { GroupviewPanel } from '../groupview/groupviewPanel';
|
||||||
|
|
||||||
export class ReactPanelDeserialzier implements IPanelDeserializer {
|
export class ReactPanelDeserialzier implements IPanelDeserializer {
|
||||||
constructor(private readonly layout: DockviewComponent) {}
|
constructor(private readonly layout: DockviewComponent) {}
|
||||||
|
|
||||||
public fromJSON(panelData: GroupviewPanelState): IGroupPanel {
|
public fromJSON(
|
||||||
|
panelData: GroupviewPanelState,
|
||||||
|
group: GroupviewPanel
|
||||||
|
): IGroupPanel {
|
||||||
const panelId = panelData.id;
|
const panelId = panelData.id;
|
||||||
const params = panelData.params;
|
const params = panelData.params;
|
||||||
const title = panelData.title;
|
const title = panelData.title;
|
||||||
@ -39,7 +43,8 @@ export class ReactPanelDeserialzier implements IPanelDeserializer {
|
|||||||
const panel = new DockviewGroupPanel(
|
const panel = new DockviewGroupPanel(
|
||||||
panelId,
|
panelId,
|
||||||
this.layout,
|
this.layout,
|
||||||
new DockviewApi(this.layout)
|
new DockviewApi(this.layout),
|
||||||
|
group
|
||||||
);
|
);
|
||||||
|
|
||||||
panel.init({
|
panel.init({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user