mirror of
https://github.com/mathuo/dockview
synced 2025-05-02 17:48:25 +00:00
Merge pull request #439 from mathuo/397-gready-rendering-mode
feat: render mode
This commit is contained in:
commit
c2ae86de3a
@ -10,6 +10,7 @@ import { PanelUpdateEvent } from '../../../../panel/types';
|
||||
import { IDockviewPanel } from '../../../../dockview/dockviewPanel';
|
||||
import { IDockviewPanelModel } from '../../../../dockview/dockviewPanelModel';
|
||||
import { DockviewComponent } from '../../../../dockview/dockviewComponent';
|
||||
import { OverlayRenderContainer } from '../../../../overlayRenderContainer';
|
||||
|
||||
class TestContentRenderer
|
||||
extends CompositeDisposable
|
||||
@ -61,6 +62,9 @@ describe('contentContainer', () => {
|
||||
const dockviewComponent = jest.fn<DockviewComponent, []>(() => {
|
||||
return {
|
||||
renderer: 'onlyWhenVisibile',
|
||||
overlayRenderContainer: new OverlayRenderContainer(
|
||||
document.createElement('div')
|
||||
),
|
||||
} as DockviewComponent;
|
||||
});
|
||||
|
||||
|
@ -21,6 +21,7 @@ import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel';
|
||||
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
||||
import { WatermarkRendererInitParameters } from '../../dockview/types';
|
||||
import { createOffsetDragOverEvent } from '../__test_utils__/utils';
|
||||
import { OverlayRenderContainer } from '../../overlayRenderContainer';
|
||||
|
||||
enum GroupChangeKind2 {
|
||||
ADD_PANEL,
|
||||
@ -227,7 +228,7 @@ export class TestPanel implements IDockviewPanel {
|
||||
}
|
||||
}
|
||||
|
||||
describe('groupview', () => {
|
||||
describe('dockviewGroupPanelModel', () => {
|
||||
let groupview: DockviewGroupPanel;
|
||||
let dockview: DockviewComponent;
|
||||
let options: GroupOptions;
|
||||
@ -250,6 +251,9 @@ describe('groupview', () => {
|
||||
removeGroup: removeGroupMock,
|
||||
onDidAddPanel: () => ({ dispose: jest.fn() }),
|
||||
onDidRemovePanel: () => ({ dispose: jest.fn() }),
|
||||
overlayRenderContainer: new OverlayRenderContainer(
|
||||
document.createElement('div')
|
||||
),
|
||||
}) as DockviewComponent;
|
||||
|
||||
groupview = new DockviewGroupPanel(dockview, 'groupview-1', options);
|
||||
@ -791,6 +795,9 @@ describe('groupview', () => {
|
||||
doSetGroupActive: jest.fn(),
|
||||
onDidAddPanel: jest.fn(),
|
||||
onDidRemovePanel: jest.fn(),
|
||||
overlayRenderContainer: new OverlayRenderContainer(
|
||||
document.createElement('div')
|
||||
),
|
||||
};
|
||||
});
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
@ -861,6 +868,9 @@ describe('groupview', () => {
|
||||
doSetGroupActive: jest.fn(),
|
||||
onDidAddPanel: jest.fn(),
|
||||
onDidRemovePanel: jest.fn(),
|
||||
overlayRenderContainer: new OverlayRenderContainer(
|
||||
document.createElement('div')
|
||||
),
|
||||
};
|
||||
});
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
@ -936,6 +946,9 @@ describe('groupview', () => {
|
||||
doSetGroupActive: jest.fn(),
|
||||
onDidAddPanel: jest.fn(),
|
||||
onDidRemovePanel: jest.fn(),
|
||||
overlayRenderContainer: new OverlayRenderContainer(
|
||||
document.createElement('div')
|
||||
),
|
||||
};
|
||||
});
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
|
@ -100,10 +100,7 @@ describe('overlayRenderContainer', () => {
|
||||
})
|
||||
);
|
||||
|
||||
const container = cut.setReferenceContentContainer(
|
||||
panel,
|
||||
referenceContainer
|
||||
);
|
||||
const container = cut.attach({ panel, referenceContainer });
|
||||
|
||||
await exhaustMicrotaskQueue();
|
||||
|
||||
|
@ -22,7 +22,7 @@ export interface IContentContainer extends IDisposable {
|
||||
closePanel: () => void;
|
||||
show(): void;
|
||||
hide(): void;
|
||||
renderPanel(panel: IDockviewPanel): void;
|
||||
renderPanel(panel: IDockviewPanel, options: { asActive: boolean }): void;
|
||||
}
|
||||
|
||||
export class ContentContainer
|
||||
@ -114,16 +114,33 @@ export class ContentContainer
|
||||
this.element.style.display = 'none';
|
||||
}
|
||||
|
||||
renderPanel(panel: IDockviewPanel): void {
|
||||
const isActive = panel === this.group.activePanel;
|
||||
renderPanel(
|
||||
panel: IDockviewPanel,
|
||||
options: { asActive: boolean } = { asActive: true }
|
||||
): void {
|
||||
const doRender =
|
||||
options.asActive ||
|
||||
(this.panel && this.group.isPanelActive(this.panel));
|
||||
|
||||
if (
|
||||
this.panel &&
|
||||
this.panel.view.content.element.parentElement === this._element
|
||||
) {
|
||||
/**
|
||||
* If the currently attached panel is mounted directly to the content then remove it
|
||||
*/
|
||||
this._element.removeChild(this.panel.view.content.element);
|
||||
}
|
||||
|
||||
this.panel = panel;
|
||||
|
||||
let container: HTMLElement;
|
||||
|
||||
switch (panel.api.renderer) {
|
||||
case 'onlyWhenVisibile':
|
||||
this.accessor.overlayRenderContainer.remove(panel);
|
||||
if (isActive) {
|
||||
if (this.panel) {
|
||||
this.accessor.overlayRenderContainer.detatch(panel);
|
||||
if (this.panel) {
|
||||
if (doRender) {
|
||||
this._element.appendChild(
|
||||
this.panel.view.content.element
|
||||
);
|
||||
@ -137,15 +154,14 @@ export class ContentContainer
|
||||
) {
|
||||
this._element.removeChild(panel.view.content.element);
|
||||
}
|
||||
container =
|
||||
this.accessor.overlayRenderContainer.setReferenceContentContainer(
|
||||
panel,
|
||||
this
|
||||
);
|
||||
container = this.accessor.overlayRenderContainer.attach({
|
||||
panel,
|
||||
referenceContainer: this,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
if (isActive) {
|
||||
if (doRender) {
|
||||
const _onDidFocus = panel.view.content.onDidFocus;
|
||||
const _onDidBlur = panel.view.content.onDidBlur;
|
||||
|
||||
@ -178,58 +194,7 @@ export class ContentContainer
|
||||
return;
|
||||
}
|
||||
|
||||
const renderer = panel.api.renderer;
|
||||
|
||||
if (
|
||||
this.panel &&
|
||||
this.panel.view.content.element.parentElement === this._element
|
||||
) {
|
||||
/**
|
||||
* If the currently attached panel is mounted directly to the content then remove it
|
||||
*/
|
||||
this._element.removeChild(this.panel.view.content.element);
|
||||
}
|
||||
|
||||
this.panel = panel;
|
||||
|
||||
let container: HTMLElement;
|
||||
|
||||
switch (renderer) {
|
||||
case 'always':
|
||||
container =
|
||||
this.accessor.overlayRenderContainer.setReferenceContentContainer(
|
||||
panel,
|
||||
this
|
||||
);
|
||||
break;
|
||||
case 'onlyWhenVisibile':
|
||||
this._element.appendChild(this.panel.view.content.element);
|
||||
container = this._element;
|
||||
break;
|
||||
}
|
||||
|
||||
const _onDidFocus = this.panel.view.content.onDidFocus;
|
||||
const _onDidBlur = this.panel.view.content.onDidBlur;
|
||||
|
||||
const disposable = new CompositeDisposable();
|
||||
const focusTracker = trackFocus(container);
|
||||
|
||||
disposable.addDisposables(
|
||||
focusTracker,
|
||||
focusTracker.onDidFocus(() => this._onDidFocus.fire()),
|
||||
focusTracker.onDidBlur(() => this._onDidBlur.fire())
|
||||
);
|
||||
|
||||
if (_onDidFocus) {
|
||||
disposable.addDisposables(
|
||||
_onDidFocus(() => this._onDidFocus.fire())
|
||||
);
|
||||
}
|
||||
if (_onDidBlur) {
|
||||
disposable.addDisposables(_onDidBlur(() => this._onDidBlur.fire()));
|
||||
}
|
||||
|
||||
this.disposable.value = disposable;
|
||||
this.renderPanel(panel);
|
||||
}
|
||||
|
||||
public layout(_width: number, _height: number): void {
|
||||
|
@ -1215,7 +1215,7 @@ export class DockviewComponent
|
||||
group.model.removePanel(panel);
|
||||
|
||||
if (!options.skipDispose) {
|
||||
this.overlayRenderContainer.remove(panel);
|
||||
this.overlayRenderContainer.detatch(panel);
|
||||
panel.dispose();
|
||||
}
|
||||
|
||||
|
@ -404,7 +404,7 @@ export class DockviewGroupPanelModel
|
||||
}
|
||||
|
||||
rerender(panel: IDockviewPanel): void {
|
||||
this.contentContainer.renderPanel(panel);
|
||||
this.contentContainer.renderPanel(panel, { asActive: false });
|
||||
}
|
||||
|
||||
public indexOf(panel: IDockviewPanel): number {
|
||||
|
@ -20,26 +20,28 @@ function createFocusableElement(): HTMLDivElement {
|
||||
export class OverlayRenderContainer extends CompositeDisposable {
|
||||
private readonly map: Record<
|
||||
string,
|
||||
{ disposable: IDisposable; element: HTMLElement }
|
||||
{
|
||||
panel: IDockviewPanel;
|
||||
disposable: IDisposable;
|
||||
destroy: IDisposable;
|
||||
element: HTMLElement;
|
||||
}
|
||||
> = {};
|
||||
|
||||
get allIds(): string[] {
|
||||
return Object.keys(this.map);
|
||||
}
|
||||
|
||||
constructor(private readonly element: HTMLElement) {
|
||||
super();
|
||||
|
||||
this.addDisposables({
|
||||
dispose: () => {
|
||||
this.addDisposables(
|
||||
Disposable.from(() => {
|
||||
for (const value of Object.values(this.map)) {
|
||||
value.disposable.dispose();
|
||||
value.destroy.dispose();
|
||||
}
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
remove(panel: IDockviewPanel): boolean {
|
||||
detatch(panel: IDockviewPanel): boolean {
|
||||
if (this.map[panel.api.id]) {
|
||||
this.map[panel.api.id].disposable.dispose();
|
||||
delete this.map[panel.api.id];
|
||||
@ -48,21 +50,25 @@ export class OverlayRenderContainer extends CompositeDisposable {
|
||||
return false;
|
||||
}
|
||||
|
||||
setReferenceContentContainer(
|
||||
panel: IDockviewPanel,
|
||||
referenceContainer: IRenderable
|
||||
): HTMLElement {
|
||||
attach(options: {
|
||||
panel: IDockviewPanel;
|
||||
referenceContainer: IRenderable;
|
||||
}): HTMLElement {
|
||||
const { panel, referenceContainer } = options;
|
||||
|
||||
if (!this.map[panel.api.id]) {
|
||||
const element = createFocusableElement();
|
||||
element.className = 'dv-render-overlay';
|
||||
|
||||
this.map[panel.api.id] = {
|
||||
panel,
|
||||
disposable: Disposable.NONE,
|
||||
destroy: Disposable.NONE,
|
||||
|
||||
element,
|
||||
};
|
||||
}
|
||||
|
||||
this.map[panel.api.id]?.disposable.dispose();
|
||||
const focusContainer = this.map[panel.api.id].element;
|
||||
|
||||
if (panel.view.content.element.parentElement !== focusContainer) {
|
||||
@ -101,6 +107,9 @@ export class OverlayRenderContainer extends CompositeDisposable {
|
||||
/**
|
||||
* since container is positioned absoutely we must explicitly forward
|
||||
* the dnd events for the expect behaviours to continue to occur in terms of dnd
|
||||
*
|
||||
* the dnd observer does not need to be conditional on whether the panel is visible since
|
||||
* non-visible panels are 'display: none' and in such case the dnd observer will not fire.
|
||||
*/
|
||||
new DragAndDropObserver(focusContainer, {
|
||||
onDragEnd: (e) => {
|
||||
@ -119,6 +128,7 @@ export class OverlayRenderContainer extends CompositeDisposable {
|
||||
referenceContainer.dropTarget.dnd.onDragOver(e);
|
||||
},
|
||||
}),
|
||||
|
||||
panel.api.onDidVisibilityChange((event) => {
|
||||
/**
|
||||
* Control the visibility of the content, however even when not visible (display: none)
|
||||
@ -133,15 +143,14 @@ export class OverlayRenderContainer extends CompositeDisposable {
|
||||
}
|
||||
|
||||
resize();
|
||||
}),
|
||||
{
|
||||
dispose: () => {
|
||||
focusContainer.removeChild(panel.view.content.element);
|
||||
this.element.removeChild(focusContainer);
|
||||
},
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.map[panel.api.id].destroy = Disposable.from(() => {
|
||||
focusContainer.removeChild(panel.view.content.element);
|
||||
this.element.removeChild(focusContainer);
|
||||
});
|
||||
|
||||
queueMicrotask(() => {
|
||||
if (this.isDisposed) {
|
||||
return;
|
||||
@ -155,6 +164,9 @@ export class OverlayRenderContainer extends CompositeDisposable {
|
||||
visibilityChanged();
|
||||
});
|
||||
|
||||
// dispose of logic asoccciated with previous reference-container
|
||||
this.map[panel.api.id].disposable.dispose();
|
||||
// and reset the disposable to the active reference-container
|
||||
this.map[panel.api.id].disposable = disposable;
|
||||
|
||||
return focusContainer;
|
||||
|
@ -7,21 +7,26 @@ import * as React from 'react';
|
||||
import { HoistedDockviewPanel } from './hoistedDockviewPanel';
|
||||
|
||||
const components = {
|
||||
iframeComponent: HoistedDockviewPanel(
|
||||
(props: IDockviewPanelProps<{ color: string }>) => {
|
||||
return (
|
||||
<iframe
|
||||
style={{
|
||||
pointerEvents: 'none',
|
||||
border: 'none',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
src="https://dockview.dev"
|
||||
/>
|
||||
);
|
||||
}
|
||||
),
|
||||
iframeComponent: (props: IDockviewPanelProps<{ color: string }>) => {
|
||||
return (
|
||||
<div style={{ height: '100%', overflow: 'auto' }}>
|
||||
<div style={{ height: '1000px', color: 'white' }}>
|
||||
{props.api.title}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<iframe
|
||||
style={{
|
||||
// pointerEvents: 'none',
|
||||
// border: 'none',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
src="https://dockview.dev"
|
||||
/>
|
||||
);
|
||||
},
|
||||
basicComponent: () => {
|
||||
return (
|
||||
<div style={{ padding: '20px', color: 'white' }}>
|
||||
@ -36,11 +41,13 @@ export const App: React.FC = (props: { theme?: string }) => {
|
||||
event.api.addPanel({
|
||||
id: 'panel_1',
|
||||
component: 'iframeComponent',
|
||||
renderer: 'always',
|
||||
});
|
||||
|
||||
event.api.addPanel({
|
||||
id: 'panel_2',
|
||||
component: 'iframeComponent',
|
||||
renderer: 'always',
|
||||
});
|
||||
|
||||
event.api.addPanel({
|
||||
|
Loading…
Reference in New Issue
Block a user