mirror of
https://github.com/mathuo/dockview
synced 2025-07-21 17:36:01 +00:00
feat: add additional methods and docs for floating groups
This commit is contained in:
parent
42b95e5f0a
commit
307780d15a
@ -5,6 +5,12 @@ import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
||||
|
||||
describe('groupPanelApi', () => {
|
||||
test('title', () => {
|
||||
const accessor: Partial<DockviewComponent> = {
|
||||
onDidAddPanel: jest.fn(),
|
||||
onDidRemovePanel: jest.fn(),
|
||||
options: {},
|
||||
};
|
||||
|
||||
const panelMock = jest.fn<DockviewPanel, []>(() => {
|
||||
return {
|
||||
update: jest.fn(),
|
||||
@ -18,7 +24,11 @@ describe('groupPanelApi', () => {
|
||||
const panel = new panelMock();
|
||||
const group = new groupMock();
|
||||
|
||||
const cut = new DockviewPanelApiImpl(panel, group);
|
||||
const cut = new DockviewPanelApiImpl(
|
||||
panel,
|
||||
group,
|
||||
<DockviewComponent>accessor
|
||||
);
|
||||
|
||||
cut.setTitle('test_title');
|
||||
expect(panel.setTitle).toBeCalledTimes(1);
|
||||
@ -44,7 +54,8 @@ describe('groupPanelApi', () => {
|
||||
|
||||
const cut = new DockviewPanelApiImpl(
|
||||
<IDockviewPanel>groupPanel,
|
||||
<DockviewGroupPanel>groupViewPanel
|
||||
<DockviewGroupPanel>groupViewPanel,
|
||||
<DockviewComponent>accessor
|
||||
);
|
||||
|
||||
cut.updateParameters({ keyA: 'valueA' });
|
||||
@ -73,7 +84,8 @@ describe('groupPanelApi', () => {
|
||||
|
||||
const cut = new DockviewPanelApiImpl(
|
||||
<IDockviewPanel>groupPanel,
|
||||
<DockviewGroupPanel>groupViewPanel
|
||||
<DockviewGroupPanel>groupViewPanel,
|
||||
<DockviewComponent>accessor
|
||||
);
|
||||
|
||||
let events = 0;
|
||||
|
@ -10,7 +10,7 @@ describe('groupDragHandler', () => {
|
||||
const groupMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||
const partial: Partial<DockviewGroupPanel> = {
|
||||
id: 'test_group_id',
|
||||
isFloating: false,
|
||||
api: { isFloating: false } as any,
|
||||
};
|
||||
return partial as DockviewGroupPanel;
|
||||
});
|
||||
@ -48,7 +48,7 @@ describe('groupDragHandler', () => {
|
||||
|
||||
const groupMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||
const partial: Partial<DockviewGroupPanel> = {
|
||||
isFloating: true,
|
||||
api: { isFloating: true } as any,
|
||||
};
|
||||
return partial as DockviewGroupPanel;
|
||||
});
|
||||
@ -76,7 +76,7 @@ describe('groupDragHandler', () => {
|
||||
|
||||
const groupMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||
const partial: Partial<DockviewGroupPanel> = {
|
||||
isFloating: false,
|
||||
api: { isFloating: false } as any,
|
||||
};
|
||||
return partial as DockviewGroupPanel;
|
||||
});
|
||||
|
@ -39,7 +39,7 @@ describe('overlay', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('#1', () => {
|
||||
test('that out-of-bounds dimensions are fixed', () => {
|
||||
const container = document.createElement('div');
|
||||
const content = document.createElement('div');
|
||||
|
||||
@ -77,6 +77,46 @@ describe('overlay', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('setBounds', () => {
|
||||
const container = document.createElement('div');
|
||||
const content = document.createElement('div');
|
||||
|
||||
document.body.appendChild(container);
|
||||
container.appendChild(content);
|
||||
|
||||
const cut = new Overlay({
|
||||
height: 1000,
|
||||
width: 1000,
|
||||
left: 0,
|
||||
top: 0,
|
||||
minimumInViewportWidth: 0,
|
||||
minimumInViewportHeight: 0,
|
||||
container,
|
||||
content,
|
||||
});
|
||||
|
||||
const element: HTMLElement = container.querySelector(
|
||||
'.dv-resize-container'
|
||||
)!;
|
||||
expect(element).toBeTruthy();
|
||||
|
||||
jest.spyOn(element, 'getBoundingClientRect').mockImplementation(() => {
|
||||
return { left: 300, top: 400, width: 1000, height: 1000 } as any;
|
||||
});
|
||||
jest.spyOn(container, 'getBoundingClientRect').mockImplementation(
|
||||
() => {
|
||||
return { left: 0, top: 0, width: 1000, height: 1000 } as any;
|
||||
}
|
||||
);
|
||||
|
||||
cut.setBounds({ height: 100, width: 200, left: 300, top: 400 });
|
||||
|
||||
expect(element.style.height).toBe('100px');
|
||||
expect(element.style.width).toBe('200px');
|
||||
expect(element.style.left).toBe('300px');
|
||||
expect(element.style.top).toBe('400px');
|
||||
});
|
||||
|
||||
test('that the resize handles are added', () => {
|
||||
const container = document.createElement('div');
|
||||
const content = document.createElement('div');
|
||||
|
@ -478,7 +478,7 @@ describe('tabsContainer', () => {
|
||||
|
||||
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||
return (<Partial<DockviewGroupPanel>>{
|
||||
isFloating: false,
|
||||
api: { isFloating: false } as any,
|
||||
}) as DockviewGroupPanel;
|
||||
});
|
||||
|
||||
@ -506,10 +506,14 @@ describe('tabsContainer', () => {
|
||||
const eventPreventDefaultSpy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(container, event);
|
||||
|
||||
expect(accessor.addFloatingGroup).toBeCalledWith(groupPanel, {
|
||||
x: 100,
|
||||
y: 60,
|
||||
});
|
||||
expect(accessor.addFloatingGroup).toBeCalledWith(
|
||||
groupPanel,
|
||||
{
|
||||
x: 100,
|
||||
y: 60,
|
||||
},
|
||||
{ inDragMode: true }
|
||||
);
|
||||
expect(accessor.addFloatingGroup).toBeCalledTimes(1);
|
||||
expect(eventPreventDefaultSpy).toBeCalledTimes(1);
|
||||
|
||||
@ -534,7 +538,7 @@ describe('tabsContainer', () => {
|
||||
|
||||
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||
return (<Partial<DockviewGroupPanel>>{
|
||||
isFloating: true,
|
||||
api: { isFloating: true } as any,
|
||||
}) as DockviewGroupPanel;
|
||||
});
|
||||
|
||||
@ -587,7 +591,7 @@ describe('tabsContainer', () => {
|
||||
|
||||
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||
return (<Partial<DockviewGroupPanel>>{
|
||||
isFloating: true,
|
||||
api: { isFloating: true } as any,
|
||||
}) as DockviewGroupPanel;
|
||||
});
|
||||
|
||||
|
@ -2795,8 +2795,8 @@ describe('dockviewComponent', () => {
|
||||
floating: true,
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
|
||||
@ -2807,8 +2807,8 @@ describe('dockviewComponent', () => {
|
||||
'right'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
});
|
||||
@ -2840,8 +2840,8 @@ describe('dockviewComponent', () => {
|
||||
floating: true,
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
|
||||
@ -2852,8 +2852,8 @@ describe('dockviewComponent', () => {
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(dockview.groups.length).toBe(1);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
});
|
||||
@ -2891,9 +2891,9 @@ describe('dockviewComponent', () => {
|
||||
floating: true,
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(3);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
|
||||
@ -2904,9 +2904,9 @@ describe('dockviewComponent', () => {
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
});
|
||||
@ -2944,9 +2944,9 @@ describe('dockviewComponent', () => {
|
||||
position: { referencePanel: panel2 },
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
|
||||
@ -2957,9 +2957,9 @@ describe('dockviewComponent', () => {
|
||||
'right'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel3.group.model.isFloating).toBeFalsy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(panel3.group.api.isFloating).toBeFalsy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
});
|
||||
@ -2997,9 +2997,9 @@ describe('dockviewComponent', () => {
|
||||
position: { referencePanel: panel2 },
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
|
||||
@ -3010,9 +3010,9 @@ describe('dockviewComponent', () => {
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel3.group.model.isFloating).toBeFalsy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(panel3.group.api.isFloating).toBeFalsy();
|
||||
expect(dockview.groups.length).toBe(1);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
});
|
||||
@ -3056,10 +3056,10 @@ describe('dockviewComponent', () => {
|
||||
floating: true,
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel4.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(panel4.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(3);
|
||||
expect(dockview.panels.length).toBe(4);
|
||||
|
||||
@ -3070,10 +3070,10 @@ describe('dockviewComponent', () => {
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel4.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(panel4.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(4);
|
||||
});
|
||||
@ -3105,8 +3105,8 @@ describe('dockviewComponent', () => {
|
||||
floating: true,
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
|
||||
@ -3117,8 +3117,8 @@ describe('dockviewComponent', () => {
|
||||
'right'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
});
|
||||
@ -3150,8 +3150,8 @@ describe('dockviewComponent', () => {
|
||||
floating: true,
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
|
||||
@ -3162,8 +3162,8 @@ describe('dockviewComponent', () => {
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(dockview.groups.length).toBe(1);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
});
|
||||
@ -3201,9 +3201,9 @@ describe('dockviewComponent', () => {
|
||||
floating: true,
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(3);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
|
||||
@ -3214,9 +3214,9 @@ describe('dockviewComponent', () => {
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
});
|
||||
@ -3254,9 +3254,9 @@ describe('dockviewComponent', () => {
|
||||
position: { referencePanel: panel2 },
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
|
||||
@ -3267,9 +3267,9 @@ describe('dockviewComponent', () => {
|
||||
'right'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(3);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
});
|
||||
@ -3307,9 +3307,9 @@ describe('dockviewComponent', () => {
|
||||
position: { referencePanel: panel2 },
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
|
||||
@ -3320,9 +3320,9 @@ describe('dockviewComponent', () => {
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
});
|
||||
@ -3366,10 +3366,10 @@ describe('dockviewComponent', () => {
|
||||
floating: true,
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel4.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(panel4.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(3);
|
||||
expect(dockview.panels.length).toBe(4);
|
||||
|
||||
@ -3380,10 +3380,10 @@ describe('dockviewComponent', () => {
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel4.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(panel4.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(3);
|
||||
expect(dockview.panels.length).toBe(4);
|
||||
});
|
||||
@ -3421,9 +3421,9 @@ describe('dockviewComponent', () => {
|
||||
floating: true,
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(3);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
|
||||
@ -3434,9 +3434,9 @@ describe('dockviewComponent', () => {
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeTruthy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeTruthy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
});
|
||||
@ -3473,9 +3473,9 @@ describe('dockviewComponent', () => {
|
||||
floating: true,
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
|
||||
@ -3486,9 +3486,9 @@ describe('dockviewComponent', () => {
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeTruthy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeTruthy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
});
|
||||
@ -3526,9 +3526,9 @@ describe('dockviewComponent', () => {
|
||||
floating: true,
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(3);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
|
||||
@ -3539,9 +3539,9 @@ describe('dockviewComponent', () => {
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeTruthy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeTruthy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
});
|
||||
@ -3578,9 +3578,9 @@ describe('dockviewComponent', () => {
|
||||
floating: true,
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
|
||||
@ -3591,9 +3591,9 @@ describe('dockviewComponent', () => {
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeTruthy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel3.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeTruthy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(panel3.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(1);
|
||||
expect(dockview.panels.length).toBe(3);
|
||||
});
|
||||
@ -3625,15 +3625,15 @@ describe('dockviewComponent', () => {
|
||||
position: { direction: 'right' },
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
|
||||
dockview.addFloatingGroup(panel2);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
});
|
||||
@ -3664,15 +3664,15 @@ describe('dockviewComponent', () => {
|
||||
component: 'default',
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(dockview.groups.length).toBe(1);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
|
||||
dockview.addFloatingGroup(panel2);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
});
|
||||
@ -3704,15 +3704,15 @@ describe('dockviewComponent', () => {
|
||||
position: { direction: 'right' },
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
|
||||
dockview.addFloatingGroup(panel2.group);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(2);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
});
|
||||
@ -3743,15 +3743,15 @@ describe('dockviewComponent', () => {
|
||||
component: 'default',
|
||||
});
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeFalsy();
|
||||
expect(panel2.group.model.isFloating).toBeFalsy();
|
||||
expect(panel1.group.api.isFloating).toBeFalsy();
|
||||
expect(panel2.group.api.isFloating).toBeFalsy();
|
||||
expect(dockview.groups.length).toBe(1);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
|
||||
dockview.addFloatingGroup(panel2.group);
|
||||
|
||||
expect(panel1.group.model.isFloating).toBeTruthy();
|
||||
expect(panel2.group.model.isFloating).toBeTruthy();
|
||||
expect(panel1.group.api.isFloating).toBeTruthy();
|
||||
expect(panel2.group.api.isFloating).toBeTruthy();
|
||||
expect(dockview.groups.length).toBe(1);
|
||||
expect(dockview.panels.length).toBe(2);
|
||||
});
|
||||
|
21
packages/dockview-core/src/__tests__/dom.spec.ts
Normal file
21
packages/dockview-core/src/__tests__/dom.spec.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { quasiDefaultPrevented, quasiPreventDefault } from '../dom';
|
||||
|
||||
describe('dom', () => {
|
||||
test('quasiPreventDefault', () => {
|
||||
const event = new Event('myevent');
|
||||
expect((event as any)['dv-quasiPreventDefault']).toBeUndefined();
|
||||
quasiPreventDefault(event);
|
||||
expect((event as any)['dv-quasiPreventDefault']).toBe(true);
|
||||
});
|
||||
|
||||
test('quasiDefaultPrevented', () => {
|
||||
const event = new Event('myevent');
|
||||
expect(quasiDefaultPrevented(event)).toBeFalsy();
|
||||
|
||||
(event as any)['dv-quasiPreventDefault'] = false;
|
||||
expect(quasiDefaultPrevented(event)).toBeFalsy();
|
||||
|
||||
(event as any)['dv-quasiPreventDefault'] = true;
|
||||
expect(quasiDefaultPrevented(event)).toBeTruthy();
|
||||
});
|
||||
});
|
@ -435,7 +435,7 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
|
||||
return this.component.addPanel(options);
|
||||
}
|
||||
|
||||
addGroup(options?: AddGroupOptions): IDockviewGroupPanel {
|
||||
addGroup(options?: AddGroupOptions): DockviewGroupPanel {
|
||||
return this.component.addGroup(options);
|
||||
}
|
||||
|
||||
@ -459,6 +459,13 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
|
||||
return this.component.getPanel(id);
|
||||
}
|
||||
|
||||
addFloatingGroup(
|
||||
item: IDockviewPanel | DockviewGroupPanel,
|
||||
coord?: { x: number; y: number }
|
||||
): void {
|
||||
return this.component.addFloatingGroup(item, coord);
|
||||
}
|
||||
|
||||
fromJSON(data: SerializedDockview): void {
|
||||
this.component.fromJSON(data);
|
||||
}
|
||||
|
54
packages/dockview-core/src/api/dockviewGroupPanelApi.ts
Normal file
54
packages/dockview-core/src/api/dockviewGroupPanelApi.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { Position } from '../dnd/droptarget';
|
||||
import { DockviewComponent } from '../dockview/dockviewComponent';
|
||||
import { DockviewGroupPanel } from '../dockview/dockviewGroupPanel';
|
||||
import { Emitter, Event } from '../events';
|
||||
import { GridviewPanelApi, GridviewPanelApiImpl } from './gridviewPanelApi';
|
||||
|
||||
export interface DockviewGroupPanelApi extends GridviewPanelApi {
|
||||
readonly onDidFloatingStateChange: Event<DockviewGroupPanelFloatingChangeEvent>;
|
||||
readonly isFloating: boolean;
|
||||
moveTo(options: { group: DockviewGroupPanel; position?: Position }): void;
|
||||
}
|
||||
|
||||
export interface DockviewGroupPanelFloatingChangeEvent {
|
||||
readonly isFloating: boolean;
|
||||
}
|
||||
|
||||
export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
|
||||
private _group: DockviewGroupPanel | undefined;
|
||||
|
||||
readonly _onDidFloatingStateChange =
|
||||
new Emitter<DockviewGroupPanelFloatingChangeEvent>();
|
||||
readonly onDidFloatingStateChange: Event<DockviewGroupPanelFloatingChangeEvent> =
|
||||
this._onDidFloatingStateChange.event;
|
||||
|
||||
get isFloating() {
|
||||
if (!this._group) {
|
||||
throw new Error(`DockviewGroupPanelApiImpl not initialized`);
|
||||
}
|
||||
return this._group.model.isFloating;
|
||||
}
|
||||
|
||||
constructor(id: string, private readonly accessor: DockviewComponent) {
|
||||
super(id);
|
||||
|
||||
this.addDisposables(this._onDidFloatingStateChange);
|
||||
}
|
||||
|
||||
moveTo(options: { group: DockviewGroupPanel; position?: Position }): void {
|
||||
if (!this._group) {
|
||||
throw new Error(`DockviewGroupPanelApiImpl not initialized`);
|
||||
}
|
||||
|
||||
this.accessor.moveGroupOrPanel(
|
||||
options.group,
|
||||
this._group.id,
|
||||
undefined,
|
||||
options.position ?? 'center'
|
||||
);
|
||||
}
|
||||
|
||||
initialize(group: DockviewGroupPanel): void {
|
||||
this._group = group;
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@ import { GridviewPanelApiImpl, GridviewPanelApi } from './gridviewPanelApi';
|
||||
import { DockviewGroupPanel } from '../dockview/dockviewGroupPanel';
|
||||
import { MutableDisposable } from '../lifecycle';
|
||||
import { IDockviewPanel } from '../dockview/dockviewPanel';
|
||||
import { DockviewComponent } from '../dockview/dockviewComponent';
|
||||
import { Position } from '../dnd/droptarget';
|
||||
|
||||
export interface TitleEvent {
|
||||
readonly title: string;
|
||||
@ -24,6 +26,11 @@ export interface DockviewPanelApi
|
||||
readonly onDidGroupChange: Event<void>;
|
||||
close(): void;
|
||||
setTitle(title: string): void;
|
||||
moveTo(options: {
|
||||
group: DockviewGroupPanel;
|
||||
position?: Position;
|
||||
index?: number;
|
||||
}): void;
|
||||
}
|
||||
|
||||
export class DockviewPanelApiImpl
|
||||
@ -73,7 +80,11 @@ export class DockviewPanelApiImpl
|
||||
return this._group;
|
||||
}
|
||||
|
||||
constructor(private panel: IDockviewPanel, group: DockviewGroupPanel) {
|
||||
constructor(
|
||||
private panel: IDockviewPanel,
|
||||
group: DockviewGroupPanel,
|
||||
private readonly accessor: DockviewComponent
|
||||
) {
|
||||
super(panel.id);
|
||||
|
||||
this.initialize(panel);
|
||||
@ -88,11 +99,25 @@ export class DockviewPanelApiImpl
|
||||
);
|
||||
}
|
||||
|
||||
public setTitle(title: string): void {
|
||||
moveTo(options: {
|
||||
group: DockviewGroupPanel;
|
||||
position?: Position;
|
||||
index?: number;
|
||||
}): void {
|
||||
this.accessor.moveGroupOrPanel(
|
||||
options.group,
|
||||
this._group.id,
|
||||
this.panel.id,
|
||||
options.position ?? 'center',
|
||||
options.index
|
||||
);
|
||||
}
|
||||
|
||||
setTitle(title: string): void {
|
||||
this.panel.setTitle(title);
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
close(): void {
|
||||
this.group.model.closePanel(this.panel);
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,11 @@ export class GroupDragHandler extends DragHandler {
|
||||
'mousedown',
|
||||
(e) => {
|
||||
if (e.shiftKey) {
|
||||
/**
|
||||
* You cannot call e.preventDefault() because that will prevent drag events from firing
|
||||
* but we also need to stop any group overlay drag events from occuring
|
||||
* Use a custom event marker that can be checked by the overlay drag events
|
||||
*/
|
||||
/**
|
||||
* You cannot call e.preventDefault() because that will prevent drag events from firing
|
||||
* but we also need to stop any group overlay drag events from occuring
|
||||
* Use a custom event marker that can be checked by the overlay drag events
|
||||
*/
|
||||
quasiPreventDefault(e);
|
||||
}
|
||||
},
|
||||
@ -37,7 +37,7 @@ export class GroupDragHandler extends DragHandler {
|
||||
}
|
||||
|
||||
override isCancelled(_event: DragEvent): boolean {
|
||||
if (this.group.isFloating && !_event.shiftKey) {
|
||||
if (this.group.api.isFloating && !_event.shiftKey) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -7,7 +7,6 @@ import {
|
||||
} from '../events';
|
||||
import { CompositeDisposable, MutableDisposable } from '../lifecycle';
|
||||
import { clamp } from '../math';
|
||||
import { getPaneData, getPanelData } from './dataTransfer';
|
||||
|
||||
const bringElementToFront = (() => {
|
||||
let previous: HTMLElement | null = null;
|
||||
@ -66,6 +65,30 @@ export class Overlay extends CompositeDisposable {
|
||||
this.renderWithinBoundaryConditions();
|
||||
}
|
||||
|
||||
setBounds(
|
||||
bounds: Partial<{
|
||||
height: number;
|
||||
width: number;
|
||||
top: number;
|
||||
left: number;
|
||||
}>
|
||||
): void {
|
||||
if (typeof bounds.height === 'number') {
|
||||
this._element.style.height = `${bounds.height}px`;
|
||||
}
|
||||
if (typeof bounds.width === 'number') {
|
||||
this._element.style.width = `${bounds.width}px`;
|
||||
}
|
||||
if (typeof bounds.top === 'number') {
|
||||
this._element.style.top = `${bounds.top}px`;
|
||||
}
|
||||
if (typeof bounds.left === 'number') {
|
||||
this._element.style.left = `${bounds.left}px`;
|
||||
}
|
||||
|
||||
this.renderWithinBoundaryConditions();
|
||||
}
|
||||
|
||||
toJSON(): { top: number; left: number; height: number; width: number } {
|
||||
const container = this.options.container.getBoundingClientRect();
|
||||
const element = this._element.getBoundingClientRect();
|
||||
@ -78,6 +101,173 @@ export class Overlay extends CompositeDisposable {
|
||||
};
|
||||
}
|
||||
|
||||
renderWithinBoundaryConditions(): void {
|
||||
const containerRect = this.options.container.getBoundingClientRect();
|
||||
const overlayRect = this._element.getBoundingClientRect();
|
||||
|
||||
// a minimum width of minimumViewportWidth must be inside the viewport
|
||||
const xOffset = Math.max(
|
||||
0,
|
||||
overlayRect.width - this.options.minimumInViewportWidth
|
||||
);
|
||||
|
||||
// a minimum height of minimumViewportHeight must be inside the viewport
|
||||
const yOffset = Math.max(
|
||||
0,
|
||||
overlayRect.height - this.options.minimumInViewportHeight
|
||||
);
|
||||
|
||||
const left = clamp(
|
||||
overlayRect.left - containerRect.left,
|
||||
-xOffset,
|
||||
Math.max(0, containerRect.width - overlayRect.width + xOffset)
|
||||
);
|
||||
|
||||
const top = clamp(
|
||||
overlayRect.top - containerRect.top,
|
||||
-yOffset,
|
||||
Math.max(0, containerRect.height - overlayRect.height + yOffset)
|
||||
);
|
||||
|
||||
this._element.style.left = `${left}px`;
|
||||
this._element.style.top = `${top}px`;
|
||||
}
|
||||
|
||||
setupDrag(
|
||||
dragTarget: HTMLElement,
|
||||
options: { inDragMode: boolean } = { inDragMode: false }
|
||||
): void {
|
||||
const move = new MutableDisposable();
|
||||
|
||||
const track = () => {
|
||||
let offset: { x: number; y: number } | null = null;
|
||||
|
||||
move.value = new CompositeDisposable(
|
||||
addDisposableWindowListener(window, 'mousemove', (e) => {
|
||||
const containerRect =
|
||||
this.options.container.getBoundingClientRect();
|
||||
const x = e.clientX - containerRect.left;
|
||||
const y = e.clientY - containerRect.top;
|
||||
|
||||
toggleClass(
|
||||
this._element,
|
||||
'dv-resize-container-dragging',
|
||||
true
|
||||
);
|
||||
|
||||
const overlayRect = this._element.getBoundingClientRect();
|
||||
if (offset === null) {
|
||||
offset = {
|
||||
x: e.clientX - overlayRect.left,
|
||||
y: e.clientY - overlayRect.top,
|
||||
};
|
||||
}
|
||||
|
||||
const xOffset = Math.max(
|
||||
0,
|
||||
overlayRect.width - this.options.minimumInViewportWidth
|
||||
);
|
||||
const yOffset = Math.max(
|
||||
0,
|
||||
overlayRect.height -
|
||||
this.options.minimumInViewportHeight
|
||||
);
|
||||
|
||||
const left = clamp(
|
||||
x - offset.x,
|
||||
-xOffset,
|
||||
Math.max(
|
||||
0,
|
||||
containerRect.width - overlayRect.width + xOffset
|
||||
)
|
||||
);
|
||||
|
||||
const top = clamp(
|
||||
y - offset.y,
|
||||
-yOffset,
|
||||
Math.max(
|
||||
0,
|
||||
containerRect.height - overlayRect.height + yOffset
|
||||
)
|
||||
);
|
||||
|
||||
this._element.style.left = `${left}px`;
|
||||
this._element.style.top = `${top}px`;
|
||||
}),
|
||||
addDisposableWindowListener(window, 'mouseup', () => {
|
||||
toggleClass(
|
||||
this._element,
|
||||
'dv-resize-container-dragging',
|
||||
false
|
||||
);
|
||||
|
||||
move.dispose();
|
||||
this._onDidChange.fire();
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
this.addDisposables(
|
||||
move,
|
||||
addDisposableListener(dragTarget, 'mousedown', (event) => {
|
||||
if (event.defaultPrevented) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// if somebody has marked this event then treat as a defaultPrevented
|
||||
// without actually calling event.preventDefault()
|
||||
if (quasiDefaultPrevented(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
track();
|
||||
}),
|
||||
addDisposableListener(
|
||||
this.options.content,
|
||||
'mousedown',
|
||||
(event) => {
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if somebody has marked this event then treat as a defaultPrevented
|
||||
// without actually calling event.preventDefault()
|
||||
if (quasiDefaultPrevented(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
track();
|
||||
}
|
||||
}
|
||||
),
|
||||
addDisposableListener(
|
||||
this.options.content,
|
||||
'mousedown',
|
||||
() => {
|
||||
bringElementToFront(this._element);
|
||||
},
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
bringElementToFront(this._element);
|
||||
|
||||
if (options.inDragMode) {
|
||||
track();
|
||||
}
|
||||
}
|
||||
|
||||
private setupOverlay(): void {
|
||||
this._element.style.height = `${this.options.height}px`;
|
||||
this._element.style.width = `${this.options.width}px`;
|
||||
this._element.style.left = `${this.options.left}px`;
|
||||
this._element.style.top = `${this.options.top}px`;
|
||||
|
||||
this._element.className = 'dv-resize-container';
|
||||
}
|
||||
|
||||
private setupResize(
|
||||
direction:
|
||||
| 'top'
|
||||
@ -252,173 +442,6 @@ export class Overlay extends CompositeDisposable {
|
||||
);
|
||||
}
|
||||
|
||||
private setupOverlay(): void {
|
||||
this._element.style.height = `${this.options.height}px`;
|
||||
this._element.style.width = `${this.options.width}px`;
|
||||
this._element.style.left = `${this.options.left}px`;
|
||||
this._element.style.top = `${this.options.top}px`;
|
||||
|
||||
this._element.className = 'dv-resize-container';
|
||||
}
|
||||
|
||||
setupDrag(
|
||||
dragTarget: HTMLElement,
|
||||
options: { inDragMode: boolean } = { inDragMode: false }
|
||||
): void {
|
||||
const move = new MutableDisposable();
|
||||
|
||||
const track = () => {
|
||||
let offset: { x: number; y: number } | null = null;
|
||||
|
||||
move.value = new CompositeDisposable(
|
||||
addDisposableWindowListener(window, 'mousemove', (e) => {
|
||||
const containerRect =
|
||||
this.options.container.getBoundingClientRect();
|
||||
const x = e.clientX - containerRect.left;
|
||||
const y = e.clientY - containerRect.top;
|
||||
|
||||
toggleClass(
|
||||
this._element,
|
||||
'dv-resize-container-dragging',
|
||||
true
|
||||
);
|
||||
|
||||
const overlayRect = this._element.getBoundingClientRect();
|
||||
if (offset === null) {
|
||||
offset = {
|
||||
x: e.clientX - overlayRect.left,
|
||||
y: e.clientY - overlayRect.top,
|
||||
};
|
||||
}
|
||||
|
||||
const xOffset = Math.max(
|
||||
0,
|
||||
overlayRect.width - this.options.minimumInViewportWidth
|
||||
);
|
||||
const yOffset = Math.max(
|
||||
0,
|
||||
overlayRect.height -
|
||||
this.options.minimumInViewportHeight
|
||||
);
|
||||
|
||||
const left = clamp(
|
||||
x - offset.x,
|
||||
-xOffset,
|
||||
Math.max(
|
||||
0,
|
||||
containerRect.width - overlayRect.width + xOffset
|
||||
)
|
||||
);
|
||||
|
||||
const top = clamp(
|
||||
y - offset.y,
|
||||
-yOffset,
|
||||
Math.max(
|
||||
0,
|
||||
containerRect.height - overlayRect.height + yOffset
|
||||
)
|
||||
);
|
||||
|
||||
this._element.style.left = `${left}px`;
|
||||
this._element.style.top = `${top}px`;
|
||||
}),
|
||||
addDisposableWindowListener(window, 'mouseup', () => {
|
||||
toggleClass(
|
||||
this._element,
|
||||
'dv-resize-container-dragging',
|
||||
false
|
||||
);
|
||||
|
||||
move.dispose();
|
||||
this._onDidChange.fire();
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
this.addDisposables(
|
||||
move,
|
||||
addDisposableListener(dragTarget, 'mousedown', (event) => {
|
||||
if (event.defaultPrevented) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// if somebody has marked this event then treat as a defaultPrevented
|
||||
// without actually calling event.preventDefault()
|
||||
if (quasiDefaultPrevented(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
track();
|
||||
}),
|
||||
addDisposableListener(
|
||||
this.options.content,
|
||||
'mousedown',
|
||||
(event) => {
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if somebody has marked this event then treat as a defaultPrevented
|
||||
// without actually calling event.preventDefault()
|
||||
if (quasiDefaultPrevented(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
track();
|
||||
}
|
||||
}
|
||||
),
|
||||
addDisposableListener(
|
||||
this.options.content,
|
||||
'mousedown',
|
||||
() => {
|
||||
bringElementToFront(this._element);
|
||||
},
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
bringElementToFront(this._element);
|
||||
|
||||
if (options.inDragMode) {
|
||||
track();
|
||||
}
|
||||
}
|
||||
|
||||
renderWithinBoundaryConditions(): void {
|
||||
const containerRect = this.options.container.getBoundingClientRect();
|
||||
const overlayRect = this._element.getBoundingClientRect();
|
||||
|
||||
// a minimum width of minimumViewportWidth must be inside the viewport
|
||||
const xOffset = Math.max(
|
||||
0,
|
||||
overlayRect.width - this.options.minimumInViewportWidth
|
||||
);
|
||||
|
||||
// a minimum height of minimumViewportHeight must be inside the viewport
|
||||
const yOffset = Math.max(
|
||||
0,
|
||||
overlayRect.height - this.options.minimumInViewportHeight
|
||||
);
|
||||
|
||||
const left = clamp(
|
||||
this.options.left,
|
||||
-xOffset,
|
||||
Math.max(0, containerRect.width - overlayRect.width + xOffset)
|
||||
);
|
||||
|
||||
const top = clamp(
|
||||
this.options.top,
|
||||
-yOffset,
|
||||
Math.max(0, containerRect.height - overlayRect.height + yOffset)
|
||||
);
|
||||
|
||||
this._element.style.left = `${left}px`;
|
||||
this._element.style.top = `${top}px`;
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
this._element.remove();
|
||||
super.dispose();
|
||||
|
@ -197,7 +197,7 @@ export class TabsContainer
|
||||
if (
|
||||
isFloatingGroupsEnabled &&
|
||||
event.shiftKey &&
|
||||
!this.group.isFloating
|
||||
!this.group.api.isFloating
|
||||
) {
|
||||
event.preventDefault();
|
||||
|
||||
@ -206,10 +206,14 @@ export class TabsContainer
|
||||
const { top: rootTop, left: rootLeft } =
|
||||
this.accessor.element.getBoundingClientRect();
|
||||
|
||||
this.accessor.addFloatingGroup(this.group, {
|
||||
x: left - rootLeft + 20,
|
||||
y: top - rootTop + 20,
|
||||
});
|
||||
this.accessor.addFloatingGroup(
|
||||
this.group,
|
||||
{
|
||||
x: left - rootLeft + 20,
|
||||
y: top - rootTop + 20,
|
||||
},
|
||||
{ inDragMode: true }
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
@ -302,10 +306,14 @@ export class TabsContainer
|
||||
const { top: rootTop, left: rootLeft } =
|
||||
this.accessor.element.getBoundingClientRect();
|
||||
|
||||
this.accessor.addFloatingGroup(panel as DockviewPanel, {
|
||||
x: left - rootLeft,
|
||||
y: top - rootTop,
|
||||
});
|
||||
this.accessor.addFloatingGroup(
|
||||
panel as DockviewPanel,
|
||||
{
|
||||
x: left - rootLeft,
|
||||
y: top - rootTop,
|
||||
},
|
||||
{ inDragMode: true }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { GroupviewPanelState } from './types';
|
||||
import { DockviewGroupPanel } from './dockviewGroupPanel';
|
||||
import { DockviewPanel, IDockviewPanel } from './dockviewPanel';
|
||||
import { IDockviewComponent } from './dockviewComponent';
|
||||
import { DockviewComponent } from './dockviewComponent';
|
||||
import { DockviewPanelModel } from './dockviewPanelModel';
|
||||
import { DockviewApi } from '../api/component.api';
|
||||
|
||||
@ -21,7 +21,7 @@ interface LegacyState extends GroupviewPanelState {
|
||||
}
|
||||
|
||||
export class DefaultDockviewDeserialzier implements IPanelDeserializer {
|
||||
constructor(private readonly layout: IDockviewComponent) {}
|
||||
constructor(private readonly layout: DockviewComponent) {}
|
||||
|
||||
public fromJSON(
|
||||
panelData: GroupviewPanelState,
|
||||
|
@ -8,6 +8,7 @@
|
||||
left: 0px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
import { directionToPosition, Droptarget, Position } from '../dnd/droptarget';
|
||||
import { tail, sequenceEquals, remove } from '../array';
|
||||
import { DockviewPanel, IDockviewPanel } from './dockviewPanel';
|
||||
import { CompositeDisposable, IDisposable } from '../lifecycle';
|
||||
import { CompositeDisposable } from '../lifecycle';
|
||||
import { Event, Emitter } from '../events';
|
||||
import { Watermark } from './components/watermark/watermark';
|
||||
import {
|
||||
@ -41,11 +41,15 @@ import {
|
||||
GroupPanelViewState,
|
||||
GroupviewDropEvent,
|
||||
} from './dockviewGroupPanelModel';
|
||||
import { DockviewGroupPanel, IDockviewGroupPanel } from './dockviewGroupPanel';
|
||||
import { DockviewGroupPanel } from './dockviewGroupPanel';
|
||||
import { DockviewPanelModel } from './dockviewPanelModel';
|
||||
import { getPanelData } from '../dnd/dataTransfer';
|
||||
import { Overlay } from '../dnd/overlay';
|
||||
import { toggleClass } from '../dom';
|
||||
import {
|
||||
DockviewFloatingGroupPanel,
|
||||
IDockviewFloatingGroupPanel,
|
||||
} from './dockviewFloatingGroupPanel';
|
||||
|
||||
export interface PanelReference {
|
||||
update: (event: { params: { [key: string]: any } }) => void;
|
||||
@ -92,6 +96,7 @@ export interface IDockviewComponent extends IBaseGrid<DockviewGroupPanel> {
|
||||
readonly activePanel: IDockviewPanel | undefined;
|
||||
readonly totalPanels: number;
|
||||
readonly panels: IDockviewPanel[];
|
||||
readonly floatingGroups: IDockviewFloatingGroupPanel[];
|
||||
readonly onDidDrop: Event<DockviewDropEvent>;
|
||||
readonly orientation: Orientation;
|
||||
updateOptions(options: DockviewComponentUpdateOptions): void;
|
||||
@ -110,7 +115,7 @@ export interface IDockviewComponent extends IBaseGrid<DockviewGroupPanel> {
|
||||
getGroupPanel: (id: string) => IDockviewPanel | undefined;
|
||||
createWatermarkComponent(): IWatermarkRenderer;
|
||||
// lifecycle
|
||||
addGroup(options?: AddGroupOptions): IDockviewGroupPanel;
|
||||
addGroup(options?: AddGroupOptions): DockviewGroupPanel;
|
||||
closeAllGroups(): void;
|
||||
// events
|
||||
moveToNext(options?: MovementOptions): void;
|
||||
@ -159,11 +164,7 @@ export class DockviewComponent
|
||||
readonly onDidActivePanelChange: Event<IDockviewPanel | undefined> =
|
||||
this._onDidActivePanelChange.event;
|
||||
|
||||
private readonly floatingGroups: {
|
||||
instance: DockviewGroupPanel;
|
||||
disposable: IDisposable;
|
||||
overlay: Overlay;
|
||||
}[] = [];
|
||||
readonly floatingGroups: DockviewFloatingGroupPanel[] = [];
|
||||
|
||||
get orientation(): Orientation {
|
||||
return this.gridview.orientation;
|
||||
@ -306,7 +307,7 @@ export class DockviewComponent
|
||||
addFloatingGroup(
|
||||
item: DockviewPanel | DockviewGroupPanel,
|
||||
coord?: { x?: number; y?: number; height?: number; width?: number },
|
||||
options?: { skipRemoveGroup: boolean; inDragMode: boolean }
|
||||
options?: { skipRemoveGroup?: boolean; inDragMode: boolean }
|
||||
): void {
|
||||
let group: DockviewGroupPanel;
|
||||
|
||||
@ -356,29 +357,30 @@ export class DockviewComponent
|
||||
inDragMode:
|
||||
typeof options?.inDragMode === 'boolean'
|
||||
? options.inDragMode
|
||||
: true,
|
||||
: false,
|
||||
});
|
||||
}
|
||||
|
||||
const instance = {
|
||||
instance: group,
|
||||
const floatingGroupPanel = new DockviewFloatingGroupPanel(
|
||||
group,
|
||||
overlay
|
||||
);
|
||||
|
||||
overlay,
|
||||
disposable: new CompositeDisposable(
|
||||
overlay,
|
||||
overlay.onDidChange(() => {
|
||||
this._bufferOnDidLayoutChange.fire();
|
||||
}),
|
||||
{
|
||||
dispose: () => {
|
||||
group.model.isFloating = false;
|
||||
remove(this.floatingGroups, instance);
|
||||
},
|
||||
}
|
||||
),
|
||||
};
|
||||
floatingGroupPanel.addDisposables(
|
||||
overlay.onDidChange(() => {
|
||||
this._bufferOnDidLayoutChange.fire();
|
||||
}),
|
||||
{
|
||||
dispose: () => {
|
||||
group.model.isFloating = false;
|
||||
remove(this.floatingGroups, floatingGroupPanel);
|
||||
this.updateWatermark();
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
this.floatingGroups.push(instance);
|
||||
this.floatingGroups.push(floatingGroupPanel);
|
||||
this.updateWatermark();
|
||||
}
|
||||
|
||||
private orthogonalize(position: Position): DockviewGroupPanel {
|
||||
@ -519,7 +521,7 @@ export class DockviewComponent
|
||||
const floats: SerializedFloatingGroup[] = this.floatingGroups.map(
|
||||
(floatingGroup) => {
|
||||
return {
|
||||
data: floatingGroup.instance.toJSON() as GroupPanelViewState,
|
||||
data: floatingGroup.group.toJSON() as GroupPanelViewState,
|
||||
position: floatingGroup.overlay.toJSON(),
|
||||
};
|
||||
}
|
||||
@ -726,7 +728,7 @@ export class DockviewComponent
|
||||
inDragMode: false,
|
||||
skipRemoveGroup: true,
|
||||
});
|
||||
} else if (referenceGroup.model.isFloating || target === 'center') {
|
||||
} else if (referenceGroup.api.isFloating || target === 'center') {
|
||||
panel = this.createPanel(options, referenceGroup);
|
||||
referenceGroup.model.openPanel(panel);
|
||||
} else {
|
||||
@ -807,7 +809,7 @@ export class DockviewComponent
|
||||
}
|
||||
|
||||
private updateWatermark(): void {
|
||||
if (this.groups.filter((x) => !x.model.isFloating).length === 0) {
|
||||
if (this.groups.filter((x) => !x.api.isFloating).length === 0) {
|
||||
if (!this.watermark) {
|
||||
this.watermark = this.createWatermarkComponent();
|
||||
|
||||
@ -920,17 +922,17 @@ export class DockviewComponent
|
||||
| undefined
|
||||
): DockviewGroupPanel {
|
||||
const floatingGroup = this.floatingGroups.find(
|
||||
(_) => _.instance === group
|
||||
(_) => _.group === group
|
||||
);
|
||||
|
||||
if (floatingGroup) {
|
||||
if (!options?.skipDispose) {
|
||||
floatingGroup.instance.dispose();
|
||||
floatingGroup.group.dispose();
|
||||
this._groups.delete(group.id);
|
||||
}
|
||||
floatingGroup.disposable.dispose();
|
||||
floatingGroup.dispose();
|
||||
|
||||
return floatingGroup.instance;
|
||||
return floatingGroup.group;
|
||||
}
|
||||
|
||||
return super.doRemoveGroup(group, options);
|
||||
@ -986,7 +988,7 @@ export class DockviewComponent
|
||||
const [targetParentLocation, to] = tail(targetLocation);
|
||||
|
||||
const isFloating = this.floatingGroups.find(
|
||||
(x) => x.instance === sourceGroup
|
||||
(x) => x.group === sourceGroup
|
||||
);
|
||||
|
||||
if (!isFloating) {
|
||||
@ -1066,11 +1068,11 @@ export class DockviewComponent
|
||||
}
|
||||
} else {
|
||||
const floatingGroup = this.floatingGroups.find(
|
||||
(x) => x.instance === sourceGroup
|
||||
(x) => x.group === sourceGroup
|
||||
);
|
||||
|
||||
if (floatingGroup) {
|
||||
floatingGroup.disposable.dispose();
|
||||
floatingGroup.dispose();
|
||||
} else {
|
||||
this.gridview.removeView(
|
||||
getGridLocation(sourceGroup.element)
|
||||
|
@ -0,0 +1,37 @@
|
||||
import { Overlay } from '../dnd/overlay';
|
||||
import { CompositeDisposable } from '../lifecycle';
|
||||
import { DockviewGroupPanel, IDockviewGroupPanel } from './dockviewGroupPanel';
|
||||
|
||||
export interface IDockviewFloatingGroupPanel {
|
||||
readonly group: IDockviewGroupPanel;
|
||||
position(
|
||||
bounds: Partial<{
|
||||
top: number;
|
||||
left: number;
|
||||
height: number;
|
||||
width: number;
|
||||
}>
|
||||
): void;
|
||||
}
|
||||
|
||||
export class DockviewFloatingGroupPanel
|
||||
extends CompositeDisposable
|
||||
implements IDockviewFloatingGroupPanel
|
||||
{
|
||||
constructor(readonly group: DockviewGroupPanel, readonly overlay: Overlay) {
|
||||
super();
|
||||
|
||||
this.addDisposables(overlay);
|
||||
}
|
||||
|
||||
position(
|
||||
bounds: Partial<{
|
||||
top: number;
|
||||
left: number;
|
||||
height: number;
|
||||
width: number;
|
||||
}>
|
||||
): void {
|
||||
this.overlay.setBounds(bounds);
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import { IFrameworkPart } from '../panel/types';
|
||||
import { DockviewComponent } from '../dockview/dockviewComponent';
|
||||
import { GridviewPanelApi } from '../api/gridviewPanelApi';
|
||||
import {
|
||||
DockviewGroupPanelModel,
|
||||
GroupOptions,
|
||||
@ -9,8 +8,13 @@ import {
|
||||
} from './dockviewGroupPanelModel';
|
||||
import { GridviewPanel, IGridviewPanel } from '../gridview/gridviewPanel';
|
||||
import { IDockviewPanel } from '../dockview/dockviewPanel';
|
||||
import {
|
||||
DockviewGroupPanelApi,
|
||||
DockviewGroupPanelApiImpl,
|
||||
} from '../api/dockviewGroupPanelApi';
|
||||
|
||||
export interface IDockviewGroupPanel extends IGridviewPanel {
|
||||
export interface IDockviewGroupPanel
|
||||
extends IGridviewPanel<DockviewGroupPanelApi> {
|
||||
model: IDockviewGroupPanelModel;
|
||||
locked: boolean;
|
||||
readonly size: number;
|
||||
@ -20,10 +24,8 @@ export interface IDockviewGroupPanel extends IGridviewPanel {
|
||||
|
||||
export type IDockviewGroupPanelPublic = IDockviewGroupPanel;
|
||||
|
||||
export type DockviewGroupPanelApi = GridviewPanelApi;
|
||||
|
||||
export class DockviewGroupPanel
|
||||
extends GridviewPanel
|
||||
extends GridviewPanel<DockviewGroupPanelApiImpl>
|
||||
implements IDockviewGroupPanel
|
||||
{
|
||||
private readonly _model: DockviewGroupPanelModel;
|
||||
@ -52,10 +54,6 @@ export class DockviewGroupPanel
|
||||
this._model.locked = value;
|
||||
}
|
||||
|
||||
get isFloating(): boolean {
|
||||
return this._model.isFloating;
|
||||
}
|
||||
|
||||
get header(): IHeader {
|
||||
return this._model.header;
|
||||
}
|
||||
@ -65,10 +63,17 @@ export class DockviewGroupPanel
|
||||
id: string,
|
||||
options: GroupOptions
|
||||
) {
|
||||
super(id, 'groupview_default', {
|
||||
minimumHeight: 100,
|
||||
minimumWidth: 100,
|
||||
});
|
||||
super(
|
||||
id,
|
||||
'groupview_default',
|
||||
{
|
||||
minimumHeight: 100,
|
||||
minimumWidth: 100,
|
||||
},
|
||||
new DockviewGroupPanelApiImpl(id, accessor)
|
||||
);
|
||||
|
||||
this.api.initialize(this); // cannot use 'this' after after 'super' call
|
||||
|
||||
this._model = new DockviewGroupPanelModel(
|
||||
this.element,
|
||||
|
@ -237,6 +237,10 @@ export class DockviewGroupPanelModel
|
||||
);
|
||||
|
||||
toggleClass(this.container, 'dv-groupview-floating', value);
|
||||
|
||||
this.groupPanel.api._onDidFloatingStateChange.fire({
|
||||
isFloating: this.isFloating,
|
||||
});
|
||||
}
|
||||
|
||||
constructor(
|
||||
|
@ -8,7 +8,7 @@ import { DockviewGroupPanel } from './dockviewGroupPanel';
|
||||
import { CompositeDisposable, IDisposable } from '../lifecycle';
|
||||
import { IPanel, PanelUpdateEvent, Parameters } from '../panel/types';
|
||||
import { IDockviewPanelModel } from './dockviewPanelModel';
|
||||
import { IDockviewComponent } from './dockviewComponent';
|
||||
import { DockviewComponent } from './dockviewComponent';
|
||||
|
||||
export interface IDockviewPanel extends IDisposable, IPanel {
|
||||
readonly view: IDockviewPanelModel;
|
||||
@ -47,7 +47,7 @@ export class DockviewPanel
|
||||
|
||||
constructor(
|
||||
public readonly id: string,
|
||||
accessor: IDockviewComponent,
|
||||
accessor: DockviewComponent,
|
||||
private readonly containerApi: DockviewApi,
|
||||
group: DockviewGroupPanel,
|
||||
readonly view: IDockviewPanelModel
|
||||
@ -55,7 +55,7 @@ export class DockviewPanel
|
||||
super();
|
||||
this._group = group;
|
||||
|
||||
this.api = new DockviewPanelApiImpl(this, this._group);
|
||||
this.api = new DockviewPanelApiImpl(this, this._group, accessor);
|
||||
|
||||
this.addDisposables(
|
||||
this.api.onActiveChange(() => {
|
||||
|
@ -8,16 +8,14 @@ import {
|
||||
IWatermarkRenderer,
|
||||
DockviewDropTargets,
|
||||
} from './types';
|
||||
import {
|
||||
DockviewGroupPanel,
|
||||
DockviewGroupPanelApi,
|
||||
} from './dockviewGroupPanel';
|
||||
import { DockviewGroupPanel } from './dockviewGroupPanel';
|
||||
import { ISplitviewStyles, Orientation } from '../splitview/splitview';
|
||||
import { PanelTransfer } from '../dnd/dataTransfer';
|
||||
import { IDisposable } from '../lifecycle';
|
||||
import { Position } from '../dnd/droptarget';
|
||||
import { IDockviewPanel } from './dockviewPanel';
|
||||
import { FrameworkFactory } from '../panel/componentFactory';
|
||||
import { DockviewGroupPanelApi } from '../api/dockviewGroupPanelApi';
|
||||
|
||||
export interface IHeaderActionsRenderer extends IDisposable {
|
||||
readonly element: HTMLElement;
|
||||
|
@ -28,8 +28,8 @@ export interface GridviewInitParameters extends PanelInitParameters {
|
||||
isVisible?: boolean;
|
||||
}
|
||||
|
||||
export interface IGridviewPanel
|
||||
extends BasePanelViewExported<GridviewPanelApi> {
|
||||
export interface IGridviewPanel<T extends GridviewPanelApi = GridviewPanelApi>
|
||||
extends BasePanelViewExported<T> {
|
||||
readonly minimumWidth: number;
|
||||
readonly maximumWidth: number;
|
||||
readonly minimumHeight: number;
|
||||
@ -38,8 +38,10 @@ export interface IGridviewPanel
|
||||
readonly snap: boolean;
|
||||
}
|
||||
|
||||
export abstract class GridviewPanel
|
||||
extends BasePanelView<GridviewPanelApiImpl>
|
||||
export abstract class GridviewPanel<
|
||||
T extends GridviewPanelApiImpl = GridviewPanelApiImpl
|
||||
>
|
||||
extends BasePanelView<T>
|
||||
implements IGridPanelComponentView, IGridviewPanel
|
||||
{
|
||||
private _evaluatedMinimumWidth = 0;
|
||||
@ -134,9 +136,10 @@ export abstract class GridviewPanel
|
||||
maximumWidth?: number;
|
||||
minimumHeight?: number;
|
||||
maximumHeight?: number;
|
||||
}
|
||||
},
|
||||
api?: T
|
||||
) {
|
||||
super(id, component, new GridviewPanelApiImpl(id));
|
||||
super(id, component, api ?? <T>new GridviewPanelApiImpl(id));
|
||||
|
||||
if (typeof options?.minimumWidth === 'number') {
|
||||
this._minimumWidth = options.minimumWidth;
|
||||
|
@ -1,7 +1,5 @@
|
||||
export * from './dnd/dataTransfer';
|
||||
|
||||
export { watchElementResize } from './dom';
|
||||
|
||||
/**
|
||||
* Events, Emitters and Disposables are very common concepts that most codebases will contain.
|
||||
* We export them with a 'Dockview' prefix here to prevent accidental use by others.
|
||||
@ -71,6 +69,10 @@ export {
|
||||
SplitviewPanelApi,
|
||||
} from './api/splitviewPanelApi';
|
||||
export { ExpansionEvent, PaneviewPanelApi } from './api/paneviewPanelApi';
|
||||
export {
|
||||
DockviewGroupPanelApi,
|
||||
DockviewGroupPanelFloatingChangeEvent,
|
||||
} from './api/dockviewGroupPanelApi';
|
||||
export {
|
||||
CommonApi,
|
||||
SplitviewApi,
|
||||
|
@ -1,3 +1,24 @@
|
||||
.dv-debug {
|
||||
.split-view-container {
|
||||
.sash-container {
|
||||
.sash {
|
||||
&.enabled {
|
||||
background-color: black;
|
||||
}
|
||||
&.disabled {
|
||||
background-color: orange;
|
||||
}
|
||||
&.maximum {
|
||||
background-color: green;
|
||||
}
|
||||
&.minimum {
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.split-view-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@ -12,22 +33,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// debug
|
||||
// .sash {
|
||||
// &.enabled {
|
||||
// background-color: black;
|
||||
// }
|
||||
// &.disabled {
|
||||
// background-color: orange;
|
||||
// }
|
||||
// &.maximum {
|
||||
// background-color: green;
|
||||
// }
|
||||
// &.minimum {
|
||||
// background-color: red;
|
||||
// }
|
||||
// }
|
||||
|
||||
&.horizontal {
|
||||
height: 100%;
|
||||
|
||||
|
@ -383,7 +383,7 @@ Floating groups can be interacted with whilst holding the `shift` key activating
|
||||
<img style={{ width: '60%' }} src={useBaseUrl('/img/float_group.svg')} />
|
||||
|
||||
Floating groups can be programatically added through the dockview `api` method `api.addFloatingGroup(...)` and you can check whether
|
||||
a group is floating via the `group.isFloating` property. See examples for full code.
|
||||
a group is floating via the `group.api.isFloating` property. See examples for full code.
|
||||
|
||||
<Container height={600} sandboxId="floatinggroup-dockview">
|
||||
<DockviewFloating />
|
||||
@ -499,6 +499,21 @@ panel.api.updateParameters({
|
||||
});
|
||||
```
|
||||
|
||||
### Move panel
|
||||
|
||||
You can programatically move a panel using the panel `api`.
|
||||
|
||||
```ts
|
||||
panel.api.moveTo({ group, position, index });
|
||||
```
|
||||
|
||||
An equivalent method for moving groups is avaliable on the group `api`.
|
||||
|
||||
```ts
|
||||
const group = panel.api.group;
|
||||
group.api.moveTo({ group, position });
|
||||
```
|
||||
|
||||
### Panel Rendering
|
||||
|
||||
By default `DockviewReact` only adds to the DOM those panels that are visible,
|
||||
|
@ -11,28 +11,6 @@ import * as ReactDOM from 'react-dom';
|
||||
import { v4 } from 'uuid';
|
||||
import './app.scss';
|
||||
|
||||
function useLocalStorageItem(key: string, defaultValue: string): string {
|
||||
const [item, setItem] = React.useState<string | null>(
|
||||
localStorage.getItem(key)
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const listener = (event: StorageEvent) => {
|
||||
setItem(localStorage.getItem(key));
|
||||
};
|
||||
|
||||
window.addEventListener('storage', listener);
|
||||
|
||||
setItem(localStorage.getItem(key));
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('storage', listener);
|
||||
};
|
||||
}, [key]);
|
||||
|
||||
return item === null ? defaultValue : item;
|
||||
}
|
||||
|
||||
const components = {
|
||||
default: (props: IDockviewPanelProps<{ title: string }>) => {
|
||||
return <div style={{ padding: '20px' }}>{props.params.title}</div>;
|
||||
@ -213,7 +191,7 @@ const LeftControls = (props: IDockviewHeaderActionsProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const DockviewDemo = () => {
|
||||
const DockviewDemo = (props: { theme?: string }) => {
|
||||
const onReady = (event: DockviewReadyEvent) => {
|
||||
event.api.addPanel({
|
||||
id: 'panel_1',
|
||||
@ -264,11 +242,6 @@ const DockviewDemo = () => {
|
||||
event.api.getPanel('panel_1')!.api.setActive();
|
||||
};
|
||||
|
||||
const theme = useLocalStorageItem(
|
||||
'dv-theme-class-name',
|
||||
'dockview-theme-abyss'
|
||||
);
|
||||
|
||||
return (
|
||||
<DockviewReact
|
||||
components={components}
|
||||
@ -276,7 +249,7 @@ const DockviewDemo = () => {
|
||||
rightHeaderActionsComponent={RightControls}
|
||||
leftHeaderActionsComponent={LeftControls}
|
||||
onReady={onReady}
|
||||
className={theme}
|
||||
className={props.theme || 'dockview-theme-abyss'}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,11 +1,14 @@
|
||||
import {
|
||||
DockviewApi,
|
||||
DockviewGroupPanel,
|
||||
DockviewReact,
|
||||
DockviewReadyEvent,
|
||||
IDockviewHeaderActionsProps,
|
||||
IDockviewPanelProps,
|
||||
SerializedDockview,
|
||||
} from 'dockview';
|
||||
import * as React from 'react';
|
||||
import { Icon } from './utils';
|
||||
|
||||
const components = {
|
||||
default: (props: IDockviewPanelProps<{ title: string }>) => {
|
||||
@ -68,12 +71,11 @@ function loadDefaultLayout(api: DockviewApi) {
|
||||
|
||||
let panelCount = 0;
|
||||
|
||||
function addFloatingPanel(api: DockviewApi) {
|
||||
function addPanel(api: DockviewApi) {
|
||||
api.addPanel({
|
||||
id: (++panelCount).toString(),
|
||||
title: `Tab ${panelCount}`,
|
||||
component: 'default',
|
||||
floating: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -203,6 +205,8 @@ export const DockviewPersistance = () => {
|
||||
onReady={onReady}
|
||||
components={components}
|
||||
watermarkComponent={Watermark}
|
||||
leftHeaderActionsComponent={LeftComponent}
|
||||
rightHeaderActionsComponent={RightComponent}
|
||||
className="dockview-theme-abyss"
|
||||
/>
|
||||
</div>
|
||||
@ -210,6 +214,51 @@ export const DockviewPersistance = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const LeftComponent = (props: IDockviewHeaderActionsProps) => {
|
||||
const onClick = () => {
|
||||
addPanel(props.containerApi);
|
||||
};
|
||||
return (
|
||||
<div style={{ height: '100%', color: 'white', padding: '0px 4px' }}>
|
||||
<Icon onClick={onClick} icon={'add'} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const RightComponent = (props: IDockviewHeaderActionsProps) => {
|
||||
const [floating, setFloating] = React.useState<boolean>(
|
||||
props.api.isFloating
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const disposable = props.group.api.onDidFloatingStateChange((event) => [
|
||||
setFloating(event.isFloating),
|
||||
]);
|
||||
|
||||
return () => {
|
||||
disposable.dispose();
|
||||
};
|
||||
}, [props.group.api]);
|
||||
|
||||
const onClick = () => {
|
||||
if (floating) {
|
||||
const group = props.containerApi.addGroup();
|
||||
props.group.api.moveTo({ group });
|
||||
} else {
|
||||
props.containerApi.addFloatingGroup(props.group);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ height: '100%', color: 'white', padding: '0px 4px' }}>
|
||||
<Icon
|
||||
onClick={onClick}
|
||||
icon={floating ? 'jump_to_element' : 'back_to_tab'}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DockviewPersistance;
|
||||
|
||||
const Watermark = () => {
|
||||
|
30
packages/docs/sandboxes/floatinggroup-dockview/src/utils.tsx
Normal file
30
packages/docs/sandboxes/floatinggroup-dockview/src/utils.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export const Icon = (props: {
|
||||
icon: string;
|
||||
title?: string;
|
||||
onClick?: (event: React.MouseEvent) => void;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
title={props.title}
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: '30px',
|
||||
height: '100%',
|
||||
|
||||
fontSize: '18px',
|
||||
}}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
<span
|
||||
style={{ fontSize: 'inherit', cursor: 'pointer' }}
|
||||
className="material-symbols-outlined"
|
||||
>
|
||||
{props.icon}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -15,7 +15,7 @@ const components = {
|
||||
},
|
||||
};
|
||||
|
||||
export const App: React.FC = () => {
|
||||
export const App: React.FC = (props: { theme?: string }) => {
|
||||
const onReady = (event: DockviewReadyEvent) => {
|
||||
const panel = event.api.addPanel({
|
||||
id: 'panel_1',
|
||||
@ -88,7 +88,7 @@ export const App: React.FC = () => {
|
||||
<DockviewReact
|
||||
components={components}
|
||||
onReady={onReady}
|
||||
className="dockview-theme-abyss"
|
||||
className={props.theme || 'dockview-theme-abyss'}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -77,6 +77,28 @@ const themes = [
|
||||
'dockview-theme-replit',
|
||||
];
|
||||
|
||||
function useLocalStorageItem(key: string, defaultValue: string): string {
|
||||
const [item, setItem] = React.useState<string | null>(
|
||||
localStorage.getItem(key)
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const listener = (event: StorageEvent) => {
|
||||
setItem(localStorage.getItem(key));
|
||||
};
|
||||
|
||||
window.addEventListener('storage', listener);
|
||||
|
||||
setItem(localStorage.getItem(key));
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('storage', listener);
|
||||
};
|
||||
}, [key]);
|
||||
|
||||
return item === null ? defaultValue : item;
|
||||
}
|
||||
|
||||
export const ThemePicker = () => {
|
||||
const [theme, setTheme] = React.useState<string>(
|
||||
localStorage.getItem('dv-theme-class-name') || themes[0]
|
||||
@ -124,6 +146,11 @@ export const MultiFrameworkContainer = (props: {
|
||||
|
||||
const [animation, setAnimation] = React.useState<boolean>(false);
|
||||
|
||||
const theme = useLocalStorageItem(
|
||||
'dv-theme-class-name',
|
||||
'dockview-theme-abyss'
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
setAnimation(true);
|
||||
|
||||
@ -182,7 +209,7 @@ export const MultiFrameworkContainer = (props: {
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
{framework === 'React' && <props.react />}
|
||||
{framework === 'React' && <props.react theme={theme} />}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
@ -226,7 +253,6 @@ export const MultiFrameworkContainer = (props: {
|
||||
</select>
|
||||
</div>
|
||||
<span style={{ flexGrow: 1 }} />
|
||||
<ThemePicker />
|
||||
<CodeSandboxButton id={sandboxId} />
|
||||
</div>
|
||||
</>
|
||||
|
Loading…
x
Reference in New Issue
Block a user