mirror of
https://github.com/mathuo/dockview
synced 2025-02-02 14:35:46 +00:00
Merge pull request #308 from mathuo/230-explore-floating-groups
230 explore floating groups
This commit is contained in:
commit
430f97fd7e
@ -96,7 +96,7 @@ describe('componentFactory', () => {
|
|||||||
|
|
||||||
expect(component).toHaveBeenCalled();
|
expect(component).toHaveBeenCalled();
|
||||||
|
|
||||||
expect(componentResult instanceof component);
|
expect(componentResult instanceof component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import { quasiDefaultPrevented, toggleClass } from '../dom';
|
import {
|
||||||
|
getElementsByTagName,
|
||||||
|
quasiDefaultPrevented,
|
||||||
|
toggleClass,
|
||||||
|
} from '../dom';
|
||||||
import {
|
import {
|
||||||
Emitter,
|
Emitter,
|
||||||
Event,
|
Event,
|
||||||
@ -29,6 +33,9 @@ export class Overlay extends CompositeDisposable {
|
|||||||
private readonly _onDidChange = new Emitter<void>();
|
private readonly _onDidChange = new Emitter<void>();
|
||||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||||
|
|
||||||
|
private readonly _onDidChangeEnd = new Emitter<void>();
|
||||||
|
readonly onDidChangeEnd: Event<void> = this._onDidChangeEnd.event;
|
||||||
|
|
||||||
private static MINIMUM_HEIGHT = 20;
|
private static MINIMUM_HEIGHT = 20;
|
||||||
private static MINIMUM_WIDTH = 20;
|
private static MINIMUM_WIDTH = 20;
|
||||||
|
|
||||||
@ -46,9 +53,10 @@ export class Overlay extends CompositeDisposable {
|
|||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.addDisposables(this._onDidChange);
|
this.addDisposables(this._onDidChange, this._onDidChangeEnd);
|
||||||
|
|
||||||
|
this._element.className = 'dv-resize-container';
|
||||||
|
|
||||||
this.setupOverlay();
|
|
||||||
this.setupResize('top');
|
this.setupResize('top');
|
||||||
this.setupResize('bottom');
|
this.setupResize('bottom');
|
||||||
this.setupResize('left');
|
this.setupResize('left');
|
||||||
@ -62,7 +70,12 @@ export class Overlay extends CompositeDisposable {
|
|||||||
this.options.container.appendChild(this._element);
|
this.options.container.appendChild(this._element);
|
||||||
|
|
||||||
// if input bad resize within acceptable boundaries
|
// if input bad resize within acceptable boundaries
|
||||||
this.renderWithinBoundaryConditions();
|
this.setBounds({
|
||||||
|
height: this.options.height,
|
||||||
|
width: this.options.width,
|
||||||
|
top: this.options.top,
|
||||||
|
left: this.options.left,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setBounds(
|
setBounds(
|
||||||
@ -71,7 +84,7 @@ export class Overlay extends CompositeDisposable {
|
|||||||
width: number;
|
width: number;
|
||||||
top: number;
|
top: number;
|
||||||
left: number;
|
left: number;
|
||||||
}>
|
}> = {}
|
||||||
): void {
|
): void {
|
||||||
if (typeof bounds.height === 'number') {
|
if (typeof bounds.height === 'number') {
|
||||||
this._element.style.height = `${bounds.height}px`;
|
this._element.style.height = `${bounds.height}px`;
|
||||||
@ -86,25 +99,11 @@ export class Overlay extends CompositeDisposable {
|
|||||||
this._element.style.left = `${bounds.left}px`;
|
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();
|
|
||||||
|
|
||||||
return {
|
|
||||||
top: element.top - container.top,
|
|
||||||
left: element.left - container.left,
|
|
||||||
width: element.width,
|
|
||||||
height: element.height,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
renderWithinBoundaryConditions(): void {
|
|
||||||
const containerRect = this.options.container.getBoundingClientRect();
|
const containerRect = this.options.container.getBoundingClientRect();
|
||||||
const overlayRect = this._element.getBoundingClientRect();
|
const overlayRect = this._element.getBoundingClientRect();
|
||||||
|
|
||||||
|
// region: ensure bounds within allowable limits
|
||||||
|
|
||||||
// a minimum width of minimumViewportWidth must be inside the viewport
|
// a minimum width of minimumViewportWidth must be inside the viewport
|
||||||
const xOffset = Math.max(
|
const xOffset = Math.max(
|
||||||
0,
|
0,
|
||||||
@ -131,6 +130,20 @@ export class Overlay extends CompositeDisposable {
|
|||||||
|
|
||||||
this._element.style.left = `${left}px`;
|
this._element.style.left = `${left}px`;
|
||||||
this._element.style.top = `${top}px`;
|
this._element.style.top = `${top}px`;
|
||||||
|
|
||||||
|
this._onDidChange.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): { top: number; left: number; height: number; width: number } {
|
||||||
|
const container = this.options.container.getBoundingClientRect();
|
||||||
|
const element = this._element.getBoundingClientRect();
|
||||||
|
|
||||||
|
return {
|
||||||
|
top: element.top - container.top,
|
||||||
|
left: element.left - container.left,
|
||||||
|
width: element.width,
|
||||||
|
height: element.height,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setupDrag(
|
setupDrag(
|
||||||
@ -142,7 +155,23 @@ export class Overlay extends CompositeDisposable {
|
|||||||
const track = () => {
|
const track = () => {
|
||||||
let offset: { x: number; y: number } | null = null;
|
let offset: { x: number; y: number } | null = null;
|
||||||
|
|
||||||
|
const iframes = [
|
||||||
|
...getElementsByTagName('iframe'),
|
||||||
|
...getElementsByTagName('webview'),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const iframe of iframes) {
|
||||||
|
iframe.style.pointerEvents = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
move.value = new CompositeDisposable(
|
move.value = new CompositeDisposable(
|
||||||
|
{
|
||||||
|
dispose: () => {
|
||||||
|
for (const iframe of iframes) {
|
||||||
|
iframe.style.pointerEvents = 'auto';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
addDisposableWindowListener(window, 'mousemove', (e) => {
|
addDisposableWindowListener(window, 'mousemove', (e) => {
|
||||||
const containerRect =
|
const containerRect =
|
||||||
this.options.container.getBoundingClientRect();
|
this.options.container.getBoundingClientRect();
|
||||||
@ -191,8 +220,7 @@ export class Overlay extends CompositeDisposable {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
this._element.style.left = `${left}px`;
|
this.setBounds({ top, left });
|
||||||
this._element.style.top = `${top}px`;
|
|
||||||
}),
|
}),
|
||||||
addDisposableWindowListener(window, 'mouseup', () => {
|
addDisposableWindowListener(window, 'mouseup', () => {
|
||||||
toggleClass(
|
toggleClass(
|
||||||
@ -202,7 +230,7 @@ export class Overlay extends CompositeDisposable {
|
|||||||
);
|
);
|
||||||
|
|
||||||
move.dispose();
|
move.dispose();
|
||||||
this._onDidChange.fire();
|
this._onDidChangeEnd.fire();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -259,15 +287,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';
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupResize(
|
private setupResize(
|
||||||
direction:
|
direction:
|
||||||
| 'top'
|
| 'top'
|
||||||
@ -297,6 +316,15 @@ export class Overlay extends CompositeDisposable {
|
|||||||
originalWidth: number;
|
originalWidth: number;
|
||||||
} | null = null;
|
} | null = null;
|
||||||
|
|
||||||
|
const iframes = [
|
||||||
|
...getElementsByTagName('iframe'),
|
||||||
|
...getElementsByTagName('webview'),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const iframe of iframes) {
|
||||||
|
iframe.style.pointerEvents = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
move.value = new CompositeDisposable(
|
move.value = new CompositeDisposable(
|
||||||
addDisposableWindowListener(window, 'mousemove', (e) => {
|
addDisposableWindowListener(window, 'mousemove', (e) => {
|
||||||
const containerRect =
|
const containerRect =
|
||||||
@ -317,10 +345,10 @@ export class Overlay extends CompositeDisposable {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let top: number | null = null;
|
let top: number | undefined = undefined;
|
||||||
let height: number | null = null;
|
let height: number | undefined = undefined;
|
||||||
let left: number | null = null;
|
let left: number | undefined = undefined;
|
||||||
let width: number | null = null;
|
let width: number | undefined = undefined;
|
||||||
|
|
||||||
const minimumInViewportHeight =
|
const minimumInViewportHeight =
|
||||||
this.options.minimumInViewportHeight;
|
this.options.minimumInViewportHeight;
|
||||||
@ -431,22 +459,18 @@ export class Overlay extends CompositeDisposable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (height !== null) {
|
this.setBounds({ height, width, top, left });
|
||||||
this._element.style.height = `${height}px`;
|
|
||||||
}
|
|
||||||
if (top !== null) {
|
|
||||||
this._element.style.top = `${top}px`;
|
|
||||||
}
|
|
||||||
if (left !== null) {
|
|
||||||
this._element.style.left = `${left}px`;
|
|
||||||
}
|
|
||||||
if (width !== null) {
|
|
||||||
this._element.style.width = `${width}px`;
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
{
|
||||||
|
dispose: () => {
|
||||||
|
for (const iframe of iframes) {
|
||||||
|
iframe.style.pointerEvents = 'auto';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
addDisposableWindowListener(window, 'mouseup', () => {
|
addDisposableWindowListener(window, 'mouseup', () => {
|
||||||
move.dispose();
|
move.dispose();
|
||||||
this._onDidChange.fire();
|
this._onDidChangeEnd.fire();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -45,7 +45,7 @@ import { DockviewGroupPanel } from './dockviewGroupPanel';
|
|||||||
import { DockviewPanelModel } from './dockviewPanelModel';
|
import { DockviewPanelModel } from './dockviewPanelModel';
|
||||||
import { getPanelData } from '../dnd/dataTransfer';
|
import { getPanelData } from '../dnd/dataTransfer';
|
||||||
import { Overlay } from '../dnd/overlay';
|
import { Overlay } from '../dnd/overlay';
|
||||||
import { toggleClass } from '../dom';
|
import { toggleClass, watchElementResize } from '../dom';
|
||||||
import {
|
import {
|
||||||
DockviewFloatingGroupPanel,
|
DockviewFloatingGroupPanel,
|
||||||
IDockviewFloatingGroupPanel,
|
IDockviewFloatingGroupPanel,
|
||||||
@ -369,8 +369,19 @@ export class DockviewComponent
|
|||||||
overlay
|
overlay
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const disposable = watchElementResize(group.element, (entry) => {
|
||||||
|
const { width, height } = entry.contentRect;
|
||||||
|
group.layout(width, height); // let the group know it's size is changing so it can fire events to the panel
|
||||||
|
});
|
||||||
|
|
||||||
floatingGroupPanel.addDisposables(
|
floatingGroupPanel.addDisposables(
|
||||||
overlay.onDidChange(() => {
|
overlay.onDidChange(() => {
|
||||||
|
// this is either a resize or a move
|
||||||
|
// to inform the panels .layout(...) the group with it's current size
|
||||||
|
// don't care about resize since the above watcher handles that
|
||||||
|
group.layout(group.height, group.width);
|
||||||
|
}),
|
||||||
|
overlay.onDidChangeEnd(() => {
|
||||||
this._bufferOnDidLayoutChange.fire();
|
this._bufferOnDidLayoutChange.fire();
|
||||||
}),
|
}),
|
||||||
group.onDidChange((event) => {
|
group.onDidChange((event) => {
|
||||||
@ -381,6 +392,8 @@ export class DockviewComponent
|
|||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
dispose: () => {
|
dispose: () => {
|
||||||
|
disposable.dispose();
|
||||||
|
|
||||||
group.model.isFloating = false;
|
group.model.isFloating = false;
|
||||||
remove(this.floatingGroups, floatingGroupPanel);
|
remove(this.floatingGroups, floatingGroupPanel);
|
||||||
this.updateWatermark();
|
this.updateWatermark();
|
||||||
@ -451,7 +464,7 @@ export class DockviewComponent
|
|||||||
if (this.floatingGroups) {
|
if (this.floatingGroups) {
|
||||||
for (const floating of this.floatingGroups) {
|
for (const floating of this.floatingGroups) {
|
||||||
// ensure floting groups stay within visible boundaries
|
// ensure floting groups stay within visible boundaries
|
||||||
floating.overlay.renderWithinBoundaryConditions();
|
floating.overlay.setBounds();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -621,7 +634,7 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const floatingGroup of this.floatingGroups) {
|
for (const floatingGroup of this.floatingGroups) {
|
||||||
floatingGroup.overlay.renderWithinBoundaryConditions();
|
floatingGroup.overlay.setBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof activeGroup === 'string') {
|
if (typeof activeGroup === 'string') {
|
||||||
|
@ -210,7 +210,6 @@ export const DockviewPersistance = (props: { theme?: string }) => {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
overflow: 'hidden',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DockviewReact
|
<DockviewReact
|
||||||
|
@ -78,6 +78,7 @@ export const HoistedDockviewPanel = <T extends object>(
|
|||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
zIndex: 999,
|
||||||
pointerEvents: 'none', // prevent this wrapper contain stealing events
|
pointerEvents: 'none', // prevent this wrapper contain stealing events
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
Loading…
Reference in New Issue
Block a user