mirror of
https://github.com/mathuo/dockview
synced 2025-02-13 11:55:45 +00:00
feat: popout windows
This commit is contained in:
parent
584795b099
commit
406af8a87f
@ -806,11 +806,14 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
|
||||
this.component.moveToPrevious(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a popout group in a new Window
|
||||
*/
|
||||
addPopoutGroup(
|
||||
item: IDockviewPanel | DockviewGroupPanel,
|
||||
options?: {
|
||||
skipRemoveGroup?: boolean;
|
||||
position?: Box;
|
||||
popoutUrl?: string;
|
||||
}
|
||||
): void {
|
||||
this.component.addPopoutGroup(item, options);
|
||||
|
@ -9,24 +9,6 @@
|
||||
.tab {
|
||||
flex-shrink: 0;
|
||||
|
||||
&:focus-within,
|
||||
&:focus {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
pointer-events: none;
|
||||
outline: 1px solid var(--dv-tab-divider-color) !important;
|
||||
outline-offset: -1px;
|
||||
z-index: 5;
|
||||
}
|
||||
}
|
||||
|
||||
&.dv-tab-dragging {
|
||||
.tab-action {
|
||||
background-color: var(--dv-activegroup-visiblepanel-tab-color);
|
||||
|
@ -58,6 +58,7 @@ import {
|
||||
GreadyRenderContainer,
|
||||
DockviewPanelRenderer,
|
||||
} from './components/greadyRenderContainer';
|
||||
import { DockviewPopoutGroupPanel } from './dockviewPopoutGroupPanel';
|
||||
|
||||
function getTheme(element: HTMLElement): string | undefined {
|
||||
function toClassList(element: HTMLElement) {
|
||||
@ -224,12 +225,6 @@ export interface DockviewDropEvent extends GroupviewDropEvent {
|
||||
group: DockviewGroupPanel | null;
|
||||
}
|
||||
|
||||
export interface DockviewPopoutGroupPanel {
|
||||
window: PopoutWindow;
|
||||
disposable: IDisposable;
|
||||
group: DockviewGroupPanel;
|
||||
}
|
||||
|
||||
export interface IDockviewComponent extends IBaseGrid<DockviewGroupPanel> {
|
||||
readonly activePanel: IDockviewPanel | undefined;
|
||||
readonly totalPanels: number;
|
||||
@ -277,8 +272,8 @@ export interface IDockviewComponent extends IBaseGrid<DockviewGroupPanel> {
|
||||
addPopoutGroup(
|
||||
item: IDockviewPanel | DockviewGroupPanel,
|
||||
options?: {
|
||||
skipRemoveGroup?: boolean;
|
||||
position?: Box;
|
||||
popoutUrl?: string;
|
||||
}
|
||||
): void;
|
||||
}
|
||||
@ -495,11 +490,10 @@ export class DockviewComponent
|
||||
options?: {
|
||||
skipRemoveGroup?: boolean;
|
||||
position?: Box;
|
||||
popoutUrl?: string;
|
||||
}
|
||||
): void {
|
||||
let group: DockviewGroupPanel;
|
||||
const theme = getTheme(this.gridview.element);
|
||||
|
||||
let box: Box | undefined = options?.position;
|
||||
|
||||
if (item instanceof DockviewPanel) {
|
||||
@ -531,42 +525,31 @@ export class DockviewComponent
|
||||
}
|
||||
}
|
||||
|
||||
// const { top: boundingTop, left: boundingLeft } =
|
||||
// this.element.getBoundingClientRect();
|
||||
const theme = getTheme(this.gridview.element);
|
||||
|
||||
const window = new PopoutWindow('test', theme ?? '', {
|
||||
url: this.options.popoutUrl ?? 'popout.html',
|
||||
left: box.left,
|
||||
top: box.top,
|
||||
width: box.width,
|
||||
height: box.height,
|
||||
const popoutWindow = new DockviewPopoutGroupPanel(group, {
|
||||
className: theme ?? '',
|
||||
popoutUrl: options?.popoutUrl ?? '/popout.html',
|
||||
box: {
|
||||
left: box.left,
|
||||
top: box.top,
|
||||
width: box.width,
|
||||
height: box.height,
|
||||
},
|
||||
});
|
||||
|
||||
const disposable = new CompositeDisposable();
|
||||
const wrappedWindow = { window, disposable, group };
|
||||
|
||||
disposable.addDisposables(
|
||||
window.onDidClose(() => {
|
||||
group.model.location = 'grid';
|
||||
|
||||
remove(this._popoutGroups, wrappedWindow);
|
||||
|
||||
this.doAddGroup(group, [0]);
|
||||
}),
|
||||
popoutWindow.addDisposables(
|
||||
{
|
||||
dispose: () => {
|
||||
group.model.location = 'grid';
|
||||
remove(this._popoutGroups, wrappedWindow);
|
||||
remove(this._popoutGroups, popoutWindow);
|
||||
},
|
||||
},
|
||||
window
|
||||
popoutWindow.window.onDidClose(() => {
|
||||
this.doAddGroup(group, [0]);
|
||||
})
|
||||
);
|
||||
|
||||
group.model.location = 'popout';
|
||||
|
||||
this._popoutGroups.push(wrappedWindow);
|
||||
|
||||
window.open(group.element);
|
||||
this._popoutGroups.push(popoutWindow);
|
||||
}
|
||||
|
||||
addFloatingGroup(
|
||||
@ -1395,7 +1378,7 @@ export class DockviewComponent
|
||||
this._onDidRemoveGroup.fire(group);
|
||||
}
|
||||
|
||||
selectedGroup.disposable.dispose();
|
||||
selectedGroup.dispose();
|
||||
|
||||
if (!options?.skipActive && this._activeGroup === group) {
|
||||
const groups = Array.from(this._groups.values());
|
||||
@ -1539,10 +1522,6 @@ export class DockviewComponent
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const floatingGroup = this._floatingGroups.find(
|
||||
(x) => x.group === sourceGroup
|
||||
);
|
||||
|
||||
switch (sourceGroup.api.location) {
|
||||
case 'grid':
|
||||
this.gridview.removeView(
|
||||
@ -1550,23 +1529,22 @@ export class DockviewComponent
|
||||
);
|
||||
break;
|
||||
case 'floating':
|
||||
const floatingGroup = this._floatingGroups.find(
|
||||
const selectedFloatingGroup = this._floatingGroups.find(
|
||||
(x) => x.group === sourceGroup
|
||||
);
|
||||
if (!floatingGroup) {
|
||||
if (!selectedFloatingGroup) {
|
||||
throw new Error('failed to find floating group');
|
||||
}
|
||||
floatingGroup.dispose();
|
||||
selectedFloatingGroup.dispose();
|
||||
break;
|
||||
case 'popout':
|
||||
const selectedGroup = this._popoutGroups.find(
|
||||
const selectedPopoutGroup = this._popoutGroups.find(
|
||||
(x) => x.group === sourceGroup
|
||||
);
|
||||
if (!selectedGroup) {
|
||||
if (!selectedPopoutGroup) {
|
||||
throw new Error('failed to find popout group');
|
||||
}
|
||||
selectedGroup.disposable.dispose();
|
||||
selectedGroup.window.dispose();
|
||||
selectedPopoutGroup.dispose();
|
||||
}
|
||||
|
||||
const referenceLocation = getGridLocation(
|
||||
|
@ -0,0 +1,43 @@
|
||||
import { CompositeDisposable } from '../lifecycle';
|
||||
import { PopoutWindow } from '../popoutWindow';
|
||||
import { Box } from '../types';
|
||||
import { DockviewGroupPanel } from './dockviewGroupPanel';
|
||||
|
||||
export class DockviewPopoutGroupPanel extends CompositeDisposable {
|
||||
readonly window: PopoutWindow;
|
||||
|
||||
constructor(
|
||||
readonly group: DockviewGroupPanel,
|
||||
private readonly options: {
|
||||
className: string;
|
||||
popoutUrl: string;
|
||||
box: Box;
|
||||
}
|
||||
) {
|
||||
super();
|
||||
|
||||
this.window = new PopoutWindow('test', options.className ?? '', {
|
||||
url: this.options.popoutUrl,
|
||||
left: this.options.box.left,
|
||||
top: this.options.box.top,
|
||||
width: this.options.box.width,
|
||||
height: this.options.box.height,
|
||||
});
|
||||
|
||||
group.model.location = 'popout';
|
||||
|
||||
this.addDisposables(
|
||||
this.window,
|
||||
{
|
||||
dispose: () => {
|
||||
group.model.location = 'grid';
|
||||
},
|
||||
},
|
||||
this.window.onDidClose(() => {
|
||||
this.dispose();
|
||||
})
|
||||
);
|
||||
|
||||
this.window.open(group.element);
|
||||
}
|
||||
}
|
@ -84,7 +84,7 @@ export class PopoutWindow extends CompositeDisposable {
|
||||
};
|
||||
|
||||
// prevent any default content from loading
|
||||
externalWindow.document.body.replaceWith(document.createElement('div'));
|
||||
// externalWindow.document.body.replaceWith(document.createElement('div'));
|
||||
|
||||
disposable.addDisposables(
|
||||
addDisposableWindowListener(window, 'beforeunload', () => {
|
||||
|
@ -371,7 +371,23 @@ You can control the bounding box of floating groups through the optional `floati
|
||||
react={DockviewFloating}
|
||||
/>
|
||||
|
||||
## Popout Window
|
||||
## Popout Groups
|
||||
|
||||
Dockview has built-in support for opening groups in new Windows.
|
||||
Each popout window can contain a single group with many panels and you can have as many popout
|
||||
windows as needed. You cannot dock multiple groups together in the same window.
|
||||
|
||||
To open an existing group in a new window
|
||||
|
||||
```tsx
|
||||
api.addPopoutGroup(group);
|
||||
```
|
||||
|
||||
From within a panel you may say
|
||||
|
||||
```tsx
|
||||
props.containerApi.addPopoutGroup(props.api.group);
|
||||
```
|
||||
|
||||
<DockviewPopoutGroup/>
|
||||
|
||||
|
@ -250,11 +250,6 @@ const LeftControls = (props: IDockviewHeaderActionsProps) => {
|
||||
const PrefixHeaderControls = (props: IDockviewHeaderActionsProps) => {
|
||||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (props.activePanel) {
|
||||
props.containerApi.addPopoutGroup(props.activePanel.group);
|
||||
}
|
||||
}}
|
||||
className="group-control"
|
||||
style={{
|
||||
display: 'flex',
|
||||
|
@ -50,7 +50,7 @@ function loadDefaultLayout(api: DockviewApi) {
|
||||
component: 'default',
|
||||
});
|
||||
|
||||
const panel4 = api.addPanel({
|
||||
api.addPanel({
|
||||
id: 'panel_4',
|
||||
component: 'default',
|
||||
});
|
||||
@ -58,7 +58,7 @@ function loadDefaultLayout(api: DockviewApi) {
|
||||
api.addPanel({
|
||||
id: 'panel_5',
|
||||
component: 'default',
|
||||
position: { referencePanel: panel4 },
|
||||
position: { direction: 'right' },
|
||||
});
|
||||
|
||||
api.addPanel({
|
||||
@ -213,13 +213,13 @@ const LeftComponent = (props: IDockviewHeaderActionsProps) => {
|
||||
};
|
||||
|
||||
const RightComponent = (props: IDockviewHeaderActionsProps) => {
|
||||
const [floating, setFloating] = React.useState<boolean>(
|
||||
props.api.position === 'popout'
|
||||
const [popout, setPopout] = React.useState<boolean>(
|
||||
props.api.location === 'popout'
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const disposable = props.group.api.onDidRenderPositionChange(
|
||||
(event) => [setFloating(event.position === 'popout')]
|
||||
(event) => [setPopout(event.location === 'popout')]
|
||||
);
|
||||
|
||||
return () => {
|
||||
@ -228,7 +228,7 @@ const RightComponent = (props: IDockviewHeaderActionsProps) => {
|
||||
}, [props.group.api]);
|
||||
|
||||
const onClick = () => {
|
||||
if (floating) {
|
||||
if (popout) {
|
||||
const group = props.containerApi.addGroup();
|
||||
props.group.api.moveTo({ group });
|
||||
} else {
|
||||
@ -240,7 +240,7 @@ const RightComponent = (props: IDockviewHeaderActionsProps) => {
|
||||
<div style={{ height: '100%', color: 'white', padding: '0px 4px' }}>
|
||||
<Icon
|
||||
onClick={onClick}
|
||||
icon={floating ? 'jump_to_element' : 'back_to_tab'}
|
||||
icon={popout ? 'jump_to_element' : 'back_to_tab'}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -359,6 +359,11 @@
|
||||
"signature": "<T extends object = Parameters>(options: AddPanelOptions<T>): IDockviewPanel",
|
||||
"type": "method"
|
||||
},
|
||||
{
|
||||
"name": "addPopoutGroup",
|
||||
"signature": "(item: IDockviewPanel | DockviewGroupPanel, options?: { position: Box, skipRemoveGroup: boolean }): void",
|
||||
"type": "method"
|
||||
},
|
||||
{
|
||||
"name": "clear",
|
||||
"comment": {
|
||||
@ -1525,11 +1530,21 @@
|
||||
"signature": "Event<void>",
|
||||
"type": "property"
|
||||
},
|
||||
{
|
||||
"name": "onDidRendererChange",
|
||||
"signature": "Event<RendererChangedEvent>",
|
||||
"type": "property"
|
||||
},
|
||||
{
|
||||
"name": "onDidVisibilityChange",
|
||||
"signature": "Event<VisibilityEvent>",
|
||||
"type": "property"
|
||||
},
|
||||
{
|
||||
"name": "renderer",
|
||||
"signature": "DockviewPanelRenderer",
|
||||
"type": "property"
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"signature": "string | undefined",
|
||||
@ -1563,6 +1578,11 @@
|
||||
"signature": "(): void",
|
||||
"type": "method"
|
||||
},
|
||||
{
|
||||
"name": "setRenderer",
|
||||
"signature": "(renderer: DockviewPanelRenderer): void",
|
||||
"type": "method"
|
||||
},
|
||||
{
|
||||
"name": "setSize",
|
||||
"signature": "(event: SizeEvent): void",
|
||||
@ -2005,6 +2025,16 @@
|
||||
"signature": "PanelCollection<IDockviewPanelProps<any>>",
|
||||
"type": "property"
|
||||
},
|
||||
{
|
||||
"name": "debug",
|
||||
"signature": "boolean",
|
||||
"type": "property"
|
||||
},
|
||||
{
|
||||
"name": "defaultRenderer",
|
||||
"signature": "DockviewPanelRenderer",
|
||||
"type": "property"
|
||||
},
|
||||
{
|
||||
"name": "defaultTabComponent",
|
||||
"signature": "FunctionComponent<IDockviewPanelHeaderProps<any>>",
|
||||
|
Loading…
Reference in New Issue
Block a user