Merge pull request #308 from mathuo/230-explore-floating-groups

230 explore floating groups
This commit is contained in:
mathuo 2023-07-20 21:34:22 +01:00 committed by GitHub
commit 430f97fd7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 92 additions and 55 deletions

View File

@ -96,7 +96,7 @@ describe('componentFactory', () => {
expect(component).toHaveBeenCalled(); expect(component).toHaveBeenCalled();
expect(componentResult instanceof component); expect(componentResult instanceof component).toBeTruthy();
}); });
}); });
}); });

View File

@ -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();
}) })
); );
}) })

View File

@ -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') {

View File

@ -210,7 +210,6 @@ export const DockviewPersistance = (props: { theme?: string }) => {
<div <div
style={{ style={{
flexGrow: 1, flexGrow: 1,
overflow: 'hidden',
}} }}
> >
<DockviewReact <DockviewReact

View File

@ -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
}} }}
> >