feat: floating group viewport rules

This commit is contained in:
mathuo 2023-08-26 23:25:40 +01:00
parent a1b2b9b1da
commit 715281b804
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
5 changed files with 76 additions and 37 deletions

View File

@ -11,6 +11,7 @@
"/packages/docs/sandboxes/dockview-app",
"/packages/docs/sandboxes/events-dockview",
"/packages/docs/sandboxes/externaldnd-dockview",
"/packages/docs/sandboxes/floatinggroup-dockview",
"/packages/docs/sandboxes/fullwidthtab-dockview",
"/packages/docs/sandboxes/groupcontol-dockview",
"/packages/docs/sandboxes/iframe-dockview",
@ -31,4 +32,4 @@
"/packages/docs/sandboxes/javascript/vanilla-dockview"
],
"node": "16"
}
}

View File

@ -1,3 +1,4 @@
import { toHaveDescription } from '@testing-library/jest-dom/matchers';
import {
getElementsByTagName,
quasiDefaultPrevented,
@ -47,8 +48,8 @@ export class Overlay extends CompositeDisposable {
top: number;
container: HTMLElement;
content: HTMLElement;
minimumInViewportWidth: number;
minimumInViewportHeight: number;
minimumInViewportWidth?: number;
minimumInViewportHeight?: number;
}
) {
super();
@ -105,16 +106,10 @@ export class Overlay extends CompositeDisposable {
// region: ensure bounds within allowable limits
// a minimum width of minimumViewportWidth must be inside the viewport
const xOffset = Math.max(
0,
overlayRect.width - this.options.minimumInViewportWidth
);
const xOffset = Math.max(0, this.getMinimumWidth(overlayRect.width));
// a minimum height of minimumViewportHeight must be inside the viewport
const yOffset = Math.max(
0,
overlayRect.height - this.options.minimumInViewportHeight
);
const yOffset = Math.max(0, this.getMinimumHeight(overlayRect.height));
const left = clamp(
overlayRect.left - containerRect.left,
@ -194,12 +189,13 @@ export class Overlay extends CompositeDisposable {
const xOffset = Math.max(
0,
overlayRect.width - this.options.minimumInViewportWidth
this.getMinimumWidth(overlayRect.width)
);
const yOffset = Math.max(
0,
overlayRect.height -
this.options.minimumInViewportHeight
this.options.minimumInViewportHeight
? this.getMinimumHeight(overlayRect.height)
: 0
);
const left = clamp(
@ -350,20 +346,16 @@ export class Overlay extends CompositeDisposable {
let left: number | undefined = undefined;
let width: number | undefined = undefined;
const minimumInViewportHeight =
this.options.minimumInViewportHeight;
const minimumInViewportWidth =
this.options.minimumInViewportWidth;
function moveTop(): void {
const moveTop = () => {
top = clamp(
y,
-Number.MAX_VALUE,
startPosition!.originalY +
startPosition!.originalHeight >
containerRect.height
? containerRect.height -
minimumInViewportHeight
? this.getMinimumHeight(
containerRect.height
)
: Math.max(
0,
startPosition!.originalY +
@ -375,31 +367,31 @@ export class Overlay extends CompositeDisposable {
startPosition!.originalY +
startPosition!.originalHeight -
top;
}
};
function moveBottom(): void {
const moveBottom = () => {
top =
startPosition!.originalY -
startPosition!.originalHeight;
height = clamp(
y - top,
top < 0
? -top + minimumInViewportHeight
top < 0 && this.options.minimumInViewportHeight
? -top +
this.options.minimumInViewportHeight
: Overlay.MINIMUM_HEIGHT,
Number.MAX_VALUE
);
}
};
function moveLeft(): void {
const moveLeft = () => {
left = clamp(
x,
-Number.MAX_VALUE,
startPosition!.originalX +
startPosition!.originalWidth >
containerRect.width
? containerRect.width -
minimumInViewportWidth
? this.getMinimumWidth(containerRect.width)
: Math.max(
0,
startPosition!.originalX +
@ -412,21 +404,22 @@ export class Overlay extends CompositeDisposable {
startPosition!.originalX +
startPosition!.originalWidth -
left;
}
};
function moveRight(): void {
const moveRight = () => {
left =
startPosition!.originalX -
startPosition!.originalWidth;
width = clamp(
x - left,
left < 0
? -left + minimumInViewportWidth
left < 0 && this.options.minimumInViewportWidth
? -left +
this.options.minimumInViewportWidth
: Overlay.MINIMUM_WIDTH,
Number.MAX_VALUE
);
}
};
switch (direction) {
case 'top':
@ -477,6 +470,20 @@ export class Overlay extends CompositeDisposable {
);
}
private getMinimumWidth(width: number) {
if (typeof this.options.minimumInViewportWidth === 'number') {
return width - this.options.minimumInViewportWidth;
}
return 0;
}
private getMinimumHeight(height: number) {
if (typeof this.options.minimumInViewportHeight === 'number') {
return height - this.options.minimumInViewportHeight;
}
return height;
}
override dispose(): void {
this._element.remove();
super.dispose();

View File

@ -87,6 +87,7 @@ export type DockviewComponentUpdateOptions = Pick<
| 'createLeftHeaderActionsElement'
| 'createRightHeaderActionsElement'
| 'disableFloatingGroups'
| 'floatingGroupsPosition'
>;
export interface DockviewDropEvent extends GroupviewDropEvent {
@ -359,8 +360,16 @@ export class DockviewComponent
width: coord?.width ?? 300,
left: overlayLeft,
top: overlayTop,
minimumInViewportWidth: 100,
minimumInViewportHeight: 100,
minimumInViewportWidth:
this.options.floatingGroupsPosition === 'boundedWithinViewport'
? undefined
: this.options.floatingGroupsPosition
?.minimumWidthWithinViewport ?? 100,
minimumInViewportHeight:
this.options.floatingGroupsPosition === 'boundedWithinViewport'
? undefined
: this.options.floatingGroupsPosition
?.minimumHeightWithinViewport ?? 100,
});
const el = group.element.querySelector('.void-container');

View File

@ -87,6 +87,12 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions {
singleTabMode?: 'fullwidth' | 'default';
parentElement?: HTMLElement;
disableFloatingGroups?: boolean;
floatingGroupsPosition?:
| 'boundedWithinViewport'
| {
minimumHeightWithinViewport?: number;
minimumWidthWithinViewport?: number;
};
}
export interface PanelOptions<P extends object = Parameters> {

View File

@ -69,6 +69,12 @@ export interface IDockviewReactProps {
leftHeaderActionsComponent?: React.FunctionComponent<IDockviewHeaderActionsProps>;
singleTabMode?: 'fullwidth' | 'default';
disableFloatingGroups?: boolean;
floatingGroupsPosition?:
| 'boundedWithinViewport'
| {
minimumHeightWithinViewport?: number;
minimumWidthWithinViewport?: number;
};
}
const DEFAULT_REACT_TAB = 'props.defaultTabComponent';
@ -162,6 +168,7 @@ export const DockviewReact = React.forwardRef(
),
singleTabMode: props.singleTabMode,
disableFloatingGroups: props.disableFloatingGroups,
floatingGroupsPosition: props.floatingGroupsPosition,
});
const { clientWidth, clientHeight } = domRef.current;
@ -205,6 +212,15 @@ export const DockviewReact = React.forwardRef(
});
}, [props.components]);
React.useEffect(() => {
if (!dockviewRef.current) {
return;
}
dockviewRef.current.updateOptions({
floatingGroupsPosition: props.floatingGroupsPosition,
});
}, [props.floatingGroupsPosition]);
React.useEffect(() => {
if (!dockviewRef.current) {
return;