mirror of
https://github.com/mathuo/dockview
synced 2025-02-02 06:25:44 +00:00
feat: window popout enhancements
This commit is contained in:
parent
6274708acb
commit
8f9d225c61
@ -111,6 +111,13 @@ describe('dockviewComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('memory leakage', () => {
|
describe('memory leakage', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
window.open = () => fromPartial<Window>({
|
||||||
|
addEventListener: jest.fn(),
|
||||||
|
close: jest.fn(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('event leakage', () => {
|
test('event leakage', () => {
|
||||||
Emitter.setLeakageMonitorEnabled(true);
|
Emitter.setLeakageMonitorEnabled(true);
|
||||||
|
|
||||||
@ -4415,6 +4422,15 @@ describe('dockviewComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('popout group', () => {
|
describe('popout group', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.spyOn(window, 'open').mockReturnValue(
|
||||||
|
fromPartial<Window>({
|
||||||
|
addEventListener: jest.fn(),
|
||||||
|
close: jest.fn(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('that can remove a popout group', () => {
|
test('that can remove a popout group', () => {
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
|
|
||||||
|
@ -833,7 +833,7 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
|
|||||||
onDidOpen?: (event: { id: string; window: Window }) => void;
|
onDidOpen?: (event: { id: string; window: Window }) => void;
|
||||||
onWillClose?: (event: { id: string; window: Window }) => void;
|
onWillClose?: (event: { id: string; window: Window }) => void;
|
||||||
}
|
}
|
||||||
): void {
|
): Promise<boolean> {
|
||||||
this.component.addPopoutGroup(item, options);
|
return this.component.addPopoutGroup(item, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,9 @@ export interface DockviewGroupPanelApi extends GridviewPanelApi {
|
|||||||
readonly onDidLocationChange: Event<DockviewGroupPanelFloatingChangeEvent>;
|
readonly onDidLocationChange: Event<DockviewGroupPanelFloatingChangeEvent>;
|
||||||
readonly location: DockviewGroupLocation;
|
readonly location: DockviewGroupLocation;
|
||||||
/**
|
/**
|
||||||
*
|
* If you require the Window object
|
||||||
* If you require the documents Window object you can call `document.defaultView`.
|
|
||||||
*
|
|
||||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/defaultView
|
|
||||||
*/
|
*/
|
||||||
getDocument(): Document;
|
getWindow(): Window;
|
||||||
moveTo(options: { group?: DockviewGroupPanel; position?: Position }): void;
|
moveTo(options: { group?: DockviewGroupPanel; position?: Position }): void;
|
||||||
maximize(): void;
|
maximize(): void;
|
||||||
isMaximized(): boolean;
|
isMaximized(): boolean;
|
||||||
@ -49,10 +46,10 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
|
|||||||
this.addDisposables(this._onDidLocationChange);
|
this.addDisposables(this._onDidLocationChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDocument(): Document {
|
getWindow(): Window {
|
||||||
return this.location.type === 'popout'
|
return this.location.type === 'popout'
|
||||||
? this.location.getWindow().document
|
? this.location.getWindow()
|
||||||
: window.document;
|
: window;
|
||||||
}
|
}
|
||||||
|
|
||||||
moveTo(options: { group?: DockviewGroupPanel; position?: Position }): void {
|
moveTo(options: { group?: DockviewGroupPanel; position?: Position }): void {
|
||||||
|
@ -44,12 +44,9 @@ export interface DockviewPanelApi
|
|||||||
isMaximized(): boolean;
|
isMaximized(): boolean;
|
||||||
exitMaximized(): void;
|
exitMaximized(): void;
|
||||||
/**
|
/**
|
||||||
*
|
* If you require the Window object
|
||||||
* If you require the documents Window object you can call `document.defaultView`.
|
|
||||||
*
|
|
||||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/defaultView
|
|
||||||
*/
|
*/
|
||||||
getDocument(): Document;
|
getWindow(): Window;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DockviewPanelApiImpl
|
export class DockviewPanelApiImpl
|
||||||
@ -145,8 +142,8 @@ export class DockviewPanelApiImpl
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDocument(): Document {
|
getWindow(): Window {
|
||||||
return this.group.api.getDocument();
|
return this.group.api.getWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
moveTo(options: {
|
moveTo(options: {
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
} from '../dnd/droptarget';
|
} from '../dnd/droptarget';
|
||||||
import { tail, sequenceEquals, remove } from '../array';
|
import { tail, sequenceEquals, remove } from '../array';
|
||||||
import { DockviewPanel, IDockviewPanel } from './dockviewPanel';
|
import { DockviewPanel, IDockviewPanel } from './dockviewPanel';
|
||||||
import { CompositeDisposable, Disposable } from '../lifecycle';
|
import { CompositeDisposable, Disposable, IDisposable } from '../lifecycle';
|
||||||
import { Event, Emitter } from '../events';
|
import { Event, Emitter } from '../events';
|
||||||
import { Watermark } from './components/watermark/watermark';
|
import { Watermark } from './components/watermark/watermark';
|
||||||
import {
|
import {
|
||||||
@ -74,7 +74,7 @@ const DEFAULT_ROOT_OVERLAY_MODEL: DroptargetOverlayModel = {
|
|||||||
size: { type: 'pixels', value: 20 },
|
size: { type: 'pixels', value: 20 },
|
||||||
};
|
};
|
||||||
|
|
||||||
function getTheme(element: HTMLElement): string | undefined {
|
function getDockviewTheme(element: HTMLElement): string | undefined {
|
||||||
function toClassList(element: HTMLElement) {
|
function toClassList(element: HTMLElement) {
|
||||||
const list: string[] = [];
|
const list: string[] = [];
|
||||||
|
|
||||||
@ -290,7 +290,7 @@ export interface IDockviewComponent extends IBaseGrid<DockviewGroupPanel> {
|
|||||||
onDidOpen?: (event: { id: string; window: Window }) => void;
|
onDidOpen?: (event: { id: string; window: Window }) => void;
|
||||||
onWillClose?: (event: { id: string; window: Window }) => void;
|
onWillClose?: (event: { id: string; window: Window }) => void;
|
||||||
}
|
}
|
||||||
): void;
|
): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DockviewComponent
|
export class DockviewComponent
|
||||||
@ -332,7 +332,11 @@ export class DockviewComponent
|
|||||||
this._onDidActivePanelChange.event;
|
this._onDidActivePanelChange.event;
|
||||||
|
|
||||||
private readonly _floatingGroups: DockviewFloatingGroupPanel[] = [];
|
private readonly _floatingGroups: DockviewFloatingGroupPanel[] = [];
|
||||||
private readonly _popoutGroups: DockviewPopoutGroupPanel[] = [];
|
private readonly _popoutGroups: {
|
||||||
|
window: PopoutWindow;
|
||||||
|
group: DockviewGroupPanel;
|
||||||
|
disposable: IDisposable;
|
||||||
|
}[] = [];
|
||||||
private readonly _rootDropTarget: Droptarget;
|
private readonly _rootDropTarget: Droptarget;
|
||||||
|
|
||||||
get orientation(): Orientation {
|
get orientation(): Orientation {
|
||||||
@ -413,7 +417,7 @@ export class DockviewComponent
|
|||||||
|
|
||||||
// iterate over a copy of the array since .dispose() mutates the original array
|
// iterate over a copy of the array since .dispose() mutates the original array
|
||||||
for (const group of [...this._popoutGroups]) {
|
for (const group of [...this._popoutGroups]) {
|
||||||
group.dispose();
|
group.disposable.dispose();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -510,7 +514,7 @@ export class DockviewComponent
|
|||||||
this.updateWatermark();
|
this.updateWatermark();
|
||||||
}
|
}
|
||||||
|
|
||||||
addPopoutGroup(
|
async addPopoutGroup(
|
||||||
item: DockviewPanel | DockviewGroupPanel,
|
item: DockviewPanel | DockviewGroupPanel,
|
||||||
options?: {
|
options?: {
|
||||||
skipRemoveGroup?: boolean;
|
skipRemoveGroup?: boolean;
|
||||||
@ -519,12 +523,59 @@ export class DockviewComponent
|
|||||||
onDidOpen?: (event: { id: string; window: Window }) => void;
|
onDidOpen?: (event: { id: string; window: Window }) => void;
|
||||||
onWillClose?: (event: { id: string; window: Window }) => void;
|
onWillClose?: (event: { id: string; window: Window }) => void;
|
||||||
}
|
}
|
||||||
): void {
|
): Promise<boolean> {
|
||||||
|
const theme = getDockviewTheme(this.gridview.element);
|
||||||
|
|
||||||
|
const getBox: () => Box = () => {
|
||||||
|
if (options?.position) {
|
||||||
|
return options.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item instanceof DockviewGroupPanel) {
|
||||||
|
return item.element.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.group) {
|
||||||
|
return item.group.element.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
return this.element.getBoundingClientRect();
|
||||||
|
};
|
||||||
|
|
||||||
|
const box: Box = getBox();
|
||||||
|
|
||||||
|
const groupId =
|
||||||
|
item instanceof DockviewGroupPanel
|
||||||
|
? item.id
|
||||||
|
: this.getNextGroupId();
|
||||||
|
|
||||||
|
const _window = new PopoutWindow(
|
||||||
|
`${this.id}-${groupId}`, // globally unique within dockview
|
||||||
|
theme ?? '',
|
||||||
|
{
|
||||||
|
url: options?.popoutUrl ?? '/popout.html',
|
||||||
|
left: window.screenX + box.left,
|
||||||
|
top: window.screenY + box.top,
|
||||||
|
width: box.width,
|
||||||
|
height: box.height,
|
||||||
|
onDidOpen: options?.onDidOpen,
|
||||||
|
onWillClose: options?.onWillClose,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const disposables = new CompositeDisposable(
|
||||||
|
_window,
|
||||||
|
_window.onDidClose(() => {
|
||||||
|
disposables.dispose();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const popoutContainer = await _window.open();
|
||||||
|
|
||||||
|
if (popoutContainer) {
|
||||||
let group: DockviewGroupPanel;
|
let group: DockviewGroupPanel;
|
||||||
let box: Box | undefined = options?.position;
|
|
||||||
|
|
||||||
if (item instanceof DockviewPanel) {
|
if (item instanceof DockviewPanel) {
|
||||||
group = this.createGroup();
|
group = this.createGroup({ id: groupId });
|
||||||
|
|
||||||
this.removePanel(item, {
|
this.removePanel(item, {
|
||||||
removeEmptyGroup: true,
|
removeEmptyGroup: true,
|
||||||
@ -532,17 +583,9 @@ export class DockviewComponent
|
|||||||
});
|
});
|
||||||
|
|
||||||
group.model.openPanel(item);
|
group.model.openPanel(item);
|
||||||
|
|
||||||
if (!box) {
|
|
||||||
box = this.element.getBoundingClientRect();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
group = item;
|
group = item;
|
||||||
|
|
||||||
if (!box) {
|
|
||||||
box = group.element.getBoundingClientRect();
|
|
||||||
}
|
|
||||||
|
|
||||||
const skip =
|
const skip =
|
||||||
typeof options?.skipRemoveGroup === 'boolean' &&
|
typeof options?.skipRemoveGroup === 'boolean' &&
|
||||||
options.skipRemoveGroup;
|
options.skipRemoveGroup;
|
||||||
@ -552,39 +595,36 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const theme = getTheme(this.gridview.element);
|
popoutContainer.appendChild(group.element);
|
||||||
|
|
||||||
const popoutWindow = new DockviewPopoutGroupPanel(
|
group.model.location = {
|
||||||
`${this.id}-${group.id}`, // globally unique within dockview
|
type: 'popout',
|
||||||
group,
|
getWindow: () => _window.window!,
|
||||||
{
|
};
|
||||||
className: theme ?? '',
|
|
||||||
popoutUrl: options?.popoutUrl ?? '/popout.html',
|
|
||||||
box: {
|
|
||||||
left: window.screenX + box.left,
|
|
||||||
top: window.screenY + box.top,
|
|
||||||
width: box.width,
|
|
||||||
height: box.height,
|
|
||||||
},
|
|
||||||
onDidOpen: options?.onDidOpen,
|
|
||||||
onWillClose: options?.onWillClose,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
popoutWindow.addDisposables(
|
const value = { window: _window, group, disposable: disposables };
|
||||||
|
|
||||||
|
disposables.addDisposables(
|
||||||
{
|
{
|
||||||
dispose: () => {
|
dispose: () => {
|
||||||
remove(this._popoutGroups, popoutWindow);
|
group.model.location = { type: 'grid' };
|
||||||
|
|
||||||
|
remove(this._popoutGroups, value);
|
||||||
this.updateWatermark();
|
this.updateWatermark();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
popoutWindow.window.onDidClose(() => {
|
_window.onDidClose(() => {
|
||||||
this.doAddGroup(group, [0]);
|
this.doAddGroup(group, [0]);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this._popoutGroups.push(popoutWindow);
|
this._popoutGroups.push(value);
|
||||||
this.updateWatermark();
|
this.updateWatermark();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
disposables.dispose();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addFloatingGroup(
|
addFloatingGroup(
|
||||||
@ -1428,7 +1468,7 @@ export class DockviewComponent
|
|||||||
this._onDidRemoveGroup.fire(group);
|
this._onDidRemoveGroup.fire(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedGroup.dispose();
|
selectedGroup.disposable.dispose();
|
||||||
|
|
||||||
if (!options?.skipActive && this._activeGroup === group) {
|
if (!options?.skipActive && this._activeGroup === group) {
|
||||||
const groups = Array.from(this._groups.values());
|
const groups = Array.from(this._groups.values());
|
||||||
@ -1595,7 +1635,7 @@ export class DockviewComponent
|
|||||||
if (!selectedPopoutGroup) {
|
if (!selectedPopoutGroup) {
|
||||||
throw new Error('failed to find popout group');
|
throw new Error('failed to find popout group');
|
||||||
}
|
}
|
||||||
selectedPopoutGroup.dispose();
|
selectedPopoutGroup.disposable.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1629,6 +1669,15 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getNextGroupId(): string {
|
||||||
|
let id = this.nextGroupId.next();
|
||||||
|
while (this._groups.has(id)) {
|
||||||
|
id = this.nextGroupId.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
createGroup(options?: GroupOptions): DockviewGroupPanel {
|
createGroup(options?: GroupOptions): DockviewGroupPanel {
|
||||||
if (!options) {
|
if (!options) {
|
||||||
options = {};
|
options = {};
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import { CompositeDisposable } from '../lifecycle';
|
import { CompositeDisposable } from '../lifecycle';
|
||||||
import { PopoutWindow } from '../popoutWindow';
|
import { PopoutWindow } from '../popoutWindow';
|
||||||
import { Box } from '../types';
|
import { Box } from '../types';
|
||||||
import { DockviewGroupPanel } from './dockviewGroupPanel';
|
|
||||||
|
|
||||||
export class DockviewPopoutGroupPanel extends CompositeDisposable {
|
export class DockviewPopoutGroupPanel extends CompositeDisposable {
|
||||||
readonly window: PopoutWindow;
|
readonly window: PopoutWindow;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly id: string,
|
readonly id: string,
|
||||||
readonly group: DockviewGroupPanel,
|
|
||||||
private readonly options: {
|
private readonly options: {
|
||||||
className: string;
|
className: string;
|
||||||
popoutUrl: string;
|
popoutUrl: string;
|
||||||
@ -29,23 +27,17 @@ export class DockviewPopoutGroupPanel extends CompositeDisposable {
|
|||||||
onWillClose: this.options.onWillClose,
|
onWillClose: this.options.onWillClose,
|
||||||
});
|
});
|
||||||
|
|
||||||
group.model.location = {
|
|
||||||
type: 'popout',
|
|
||||||
getWindow: () => this.window.window!,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this.window,
|
this.window,
|
||||||
{
|
|
||||||
dispose: () => {
|
|
||||||
group.model.location = { type: 'grid' };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
this.window.onDidClose(() => {
|
this.window.onDidClose(() => {
|
||||||
this.dispose();
|
this.dispose();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
this.window.open(group.element);
|
open(): Promise<HTMLElement | null> {
|
||||||
|
const didOpen = this.window.open();
|
||||||
|
|
||||||
|
return didOpen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ export class PopoutWindow extends CompositeDisposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open(content: HTMLElement): void {
|
async open(): Promise<HTMLElement | null> {
|
||||||
if (this._window) {
|
if (this._window) {
|
||||||
throw new Error('instance of popout window is already open');
|
throw new Error('instance of popout window is already open');
|
||||||
}
|
}
|
||||||
@ -88,9 +88,13 @@ export class PopoutWindow extends CompositeDisposable {
|
|||||||
const externalWindow = window.open(url, this.target, features);
|
const externalWindow = window.open(url, this.target, features);
|
||||||
|
|
||||||
if (!externalWindow) {
|
if (!externalWindow) {
|
||||||
return;
|
/**
|
||||||
|
* Popup blocked
|
||||||
|
*/
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const disposable = new CompositeDisposable();
|
const disposable = new CompositeDisposable();
|
||||||
|
|
||||||
this._window = { value: externalWindow, disposable };
|
this._window = { value: externalWindow, disposable };
|
||||||
@ -104,9 +108,24 @@ export class PopoutWindow extends CompositeDisposable {
|
|||||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
|
||||||
*/
|
*/
|
||||||
this.close();
|
this.close();
|
||||||
|
}),
|
||||||
|
addDisposableWindowListener(externalWindow, 'beforeunload', () => {
|
||||||
|
/**
|
||||||
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
|
||||||
|
*/
|
||||||
|
this.close();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const container = this.createPopoutWindowContainer();
|
||||||
|
container.classList.add(this.className);
|
||||||
|
|
||||||
|
this.options.onDidOpen?.({
|
||||||
|
id: this.target,
|
||||||
|
window: externalWindow,
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise<HTMLElement | null>((resolve) => {
|
||||||
externalWindow.addEventListener('load', () => {
|
externalWindow.addEventListener('load', () => {
|
||||||
/**
|
/**
|
||||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
|
||||||
@ -115,25 +134,15 @@ export class PopoutWindow extends CompositeDisposable {
|
|||||||
const externalDocument = externalWindow.document;
|
const externalDocument = externalWindow.document;
|
||||||
externalDocument.title = document.title;
|
externalDocument.title = document.title;
|
||||||
|
|
||||||
const container = this.createPopoutWindowContainer();
|
|
||||||
container.classList.add(this.className);
|
|
||||||
container.appendChild(content);
|
|
||||||
|
|
||||||
// externalDocument.body.replaceChildren(container);
|
// externalDocument.body.replaceChildren(container);
|
||||||
externalDocument.body.appendChild(container);
|
externalDocument.body.appendChild(container);
|
||||||
externalDocument.body.classList.add(this.className);
|
externalDocument.body.classList.add(this.className);
|
||||||
|
|
||||||
addStyles(externalDocument, window.document.styleSheets);
|
addStyles(externalDocument, window.document.styleSheets);
|
||||||
|
|
||||||
externalWindow.addEventListener('beforeunload', () => {
|
resolve(container);
|
||||||
/**
|
|
||||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
|
|
||||||
*/
|
|
||||||
this.close();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.options.onDidOpen?.({ id: this.target, window: externalWindow });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private createPopoutWindowContainer(): HTMLElement {
|
private createPopoutWindowContainer(): HTMLElement {
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
"dockview": "^1.9.2",
|
"dockview": "^1.9.2",
|
||||||
"prism-react-renderer": "^2.3.1",
|
"prism-react-renderer": "^2.3.1",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
|
"react-laag": "^2.0.5",
|
||||||
"recoil": "^0.7.7",
|
"recoil": "^0.7.7",
|
||||||
"source-map-loader": "^4.0.2",
|
"source-map-loader": "^4.0.2",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
|
@ -6,49 +6,30 @@ import {
|
|||||||
IDockviewPanelProps,
|
IDockviewPanelProps,
|
||||||
SerializedDockview,
|
SerializedDockview,
|
||||||
DockviewPanelApi,
|
DockviewPanelApi,
|
||||||
DockviewGroupLocation,
|
|
||||||
} from 'dockview';
|
} from 'dockview';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as ReactDOM from 'react-dom';
|
|
||||||
import { Icon } from './utils';
|
import { Icon } from './utils';
|
||||||
import { PopoverMenu } from './popover';
|
import { PopoverMenu } from './popover';
|
||||||
|
|
||||||
function usePopoutWindowContext(api: DockviewPanelApi): Window {
|
function usePanelWindowObject(api: DockviewPanelApi): Window {
|
||||||
const [location, setLocation] = React.useState<DockviewGroupLocation>(
|
const [document, setDocument] = React.useState<Window>(api.getWindow());
|
||||||
api.location
|
|
||||||
);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const disposable = api.onDidLocationChange((event) => {
|
const disposable = api.onDidLocationChange((event) => {
|
||||||
setLocation(event.location);
|
setDocument(api.getWindow());
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
disposable.dispose();
|
disposable.dispose();
|
||||||
};
|
};
|
||||||
});
|
}, [api]);
|
||||||
|
|
||||||
const windowContext = React.useMemo(() => {
|
return document;
|
||||||
if (location.type === 'popout') {
|
|
||||||
return location.getWindow();
|
|
||||||
}
|
|
||||||
return window;
|
|
||||||
}, [location]);
|
|
||||||
|
|
||||||
return windowContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
default: (props: IDockviewPanelProps<{ title: string }>) => {
|
default: (props: IDockviewPanelProps<{ title: string }>) => {
|
||||||
const windowContext = usePopoutWindowContext(props.api);
|
const _window = usePanelWindowObject(props.api);
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
const a = windowContext.document.createElement('div');
|
|
||||||
a.className = 'aaa';
|
|
||||||
windowContext.document.body.appendChild(a);
|
|
||||||
}, 5000);
|
|
||||||
}, [windowContext]);
|
|
||||||
|
|
||||||
const [reset, setReset] = React.useState<boolean>(false);
|
const [reset, setReset] = React.useState<boolean>(false);
|
||||||
|
|
||||||
@ -62,7 +43,7 @@ const components = {
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
console.log(windowContext);
|
console.log(_window);
|
||||||
setReset(true);
|
setReset(true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setReset(false);
|
setReset(false);
|
||||||
@ -71,7 +52,7 @@ const components = {
|
|||||||
>
|
>
|
||||||
Print
|
Print
|
||||||
</button>
|
</button>
|
||||||
{!reset && <PopoverMenu api={props.api} />}
|
{!reset && <PopoverMenu window={_window} />}
|
||||||
{props.api.title}
|
{props.api.title}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -258,12 +239,12 @@ const LeftComponent = (props: IDockviewHeaderActionsProps) => {
|
|||||||
|
|
||||||
const RightComponent = (props: IDockviewHeaderActionsProps) => {
|
const RightComponent = (props: IDockviewHeaderActionsProps) => {
|
||||||
const [popout, setPopout] = React.useState<boolean>(
|
const [popout, setPopout] = React.useState<boolean>(
|
||||||
props.api.location === 'popout'
|
props.api.location.type === 'popout'
|
||||||
);
|
);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const disposable = props.group.api.onDidLocationChange((event) => [
|
const disposable = props.group.api.onDidLocationChange((event) => [
|
||||||
setPopout(event.location === 'popout'),
|
setPopout(event.location.type === 'popout'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
@ -3,7 +3,7 @@ import { motion, AnimatePresence } from 'framer-motion';
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { DockviewPanelApi } from 'dockview';
|
import { DockviewPanelApi } from 'dockview';
|
||||||
|
|
||||||
export function PopoverMenu(props: { api: DockviewPanelApi }) {
|
export function PopoverMenu(props: { window: Window }) {
|
||||||
const [isOpen, setOpen] = React.useState(false);
|
const [isOpen, setOpen] = React.useState(false);
|
||||||
|
|
||||||
// helper function to close the menu
|
// helper function to close the menu
|
||||||
@ -11,11 +11,6 @@ export function PopoverMenu(props: { api: DockviewPanelApi }) {
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const _window =
|
|
||||||
props.api.location.type === 'popout'
|
|
||||||
? props.api.location.getWindow()
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const { renderLayer, triggerProps, layerProps, arrowProps } = useLayer({
|
const { renderLayer, triggerProps, layerProps, arrowProps } = useLayer({
|
||||||
isOpen,
|
isOpen,
|
||||||
onOutsideClick: close, // close the menu when the user clicks outside
|
onOutsideClick: close, // close the menu when the user clicks outside
|
||||||
@ -26,15 +21,14 @@ export function PopoverMenu(props: { api: DockviewPanelApi }) {
|
|||||||
triggerOffset: 12, // keep some distance to the trigger
|
triggerOffset: 12, // keep some distance to the trigger
|
||||||
containerOffset: 16, // give the menu some room to breath relative to the container
|
containerOffset: 16, // give the menu some room to breath relative to the container
|
||||||
arrowOffset: 16, // let the arrow have some room to breath also,
|
arrowOffset: 16, // let the arrow have some room to breath also,
|
||||||
environment: _window,
|
environment: props.window,
|
||||||
container: _window
|
container: props.window
|
||||||
? () => {
|
? () => {
|
||||||
const el = _window.document.body;
|
const el = props.window.document.body;
|
||||||
Object.setPrototypeOf(el, HTMLElement.prototype);
|
Object.setPrototypeOf(el, HTMLElement.prototype);
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
// container: props.window.document.body
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Again, we're using framer-motion for the transition effect
|
// Again, we're using framer-motion for the transition effect
|
||||||
|
Loading…
Reference in New Issue
Block a user