diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 52156e5a1..986267fe3 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -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" -} \ No newline at end of file +} diff --git a/packages/dockview-core/src/dnd/overlay.ts b/packages/dockview-core/src/dnd/overlay.ts index 29fb4376f..236726f1b 100644 --- a/packages/dockview-core/src/dnd/overlay.ts +++ b/packages/dockview-core/src/dnd/overlay.ts @@ -1,3 +1,4 @@ +import { toHaveDescription } from '@testing-library/jest-dom/matchers'; import { getElementsByTagName, quasiDefaultPrevented, @@ -39,6 +40,14 @@ export class Overlay extends CompositeDisposable { private static MINIMUM_HEIGHT = 20; private static MINIMUM_WIDTH = 20; + set minimumInViewportWidth(value: number | undefined) { + this.options.minimumInViewportWidth = value; + } + + set minimumInViewportHeight(value: number | undefined) { + this.options.minimumInViewportHeight = value; + } + constructor( private readonly options: { height: number; @@ -47,8 +56,8 @@ export class Overlay extends CompositeDisposable { top: number; container: HTMLElement; content: HTMLElement; - minimumInViewportWidth: number; - minimumInViewportHeight: number; + minimumInViewportWidth?: number; + minimumInViewportHeight?: number; } ) { super(); @@ -105,16 +114,13 @@ 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 = + typeof this.options.minimumInViewportHeight === 'number' + ? Math.max(0, this.getMinimumHeight(overlayRect.height)) + : 0; const left = clamp( overlayRect.left - containerRect.left, @@ -194,12 +200,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 +357,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 +378,33 @@ 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 && + typeof this.options + .minimumInViewportHeight === 'number' + ? -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 +417,24 @@ 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 && + typeof this.options + .minimumInViewportWidth === 'number' + ? -left + + this.options.minimumInViewportWidth : Overlay.MINIMUM_WIDTH, Number.MAX_VALUE ); - } + }; switch (direction) { case 'top': @@ -477,6 +485,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(); diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index 118fc9632..04a0eb280 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -56,6 +56,8 @@ import { TabDragEvent, } from './components/titlebar/tabsContainer'; +const DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE = 100; + export interface PanelReference { update: (event: { params: { [key: string]: any } }) => void; remove: () => void; @@ -91,6 +93,7 @@ export type DockviewComponentUpdateOptions = Pick< | 'createLeftHeaderActionsElement' | 'createRightHeaderActionsElement' | 'disableFloatingGroups' + | 'floatingGroupBounds' >; export interface DockviewDropEvent extends GroupviewDropEvent { @@ -378,8 +381,18 @@ export class DockviewComponent width: coord?.width ?? 300, left: overlayLeft, top: overlayTop, - minimumInViewportWidth: 100, - minimumInViewportHeight: 100, + minimumInViewportWidth: + this.options.floatingGroupBounds === 'boundedWithinViewport' + ? undefined + : this.options.floatingGroupBounds + ?.minimumWidthWithinViewport ?? + DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE, + minimumInViewportHeight: + this.options.floatingGroupBounds === 'boundedWithinViewport' + ? undefined + : this.options.floatingGroupBounds + ?.minimumHeightWithinViewport ?? + DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE, }); const el = group.element.querySelector('.void-container'); @@ -475,6 +488,9 @@ export class DockviewComponent const hasOrientationChanged = typeof options.orientation === 'string' && this.gridview.orientation !== options.orientation; + const hasFloatingGroupOptionsChanged = + options.floatingGroupBounds !== undefined && + options.floatingGroupBounds !== this.options.floatingGroupBounds; this._options = { ...this.options, ...options }; @@ -482,6 +498,30 @@ export class DockviewComponent this.gridview.orientation = options.orientation!; } + if (hasFloatingGroupOptionsChanged) { + for (const group of this.floatingGroups) { + switch (this.options.floatingGroupBounds) { + case 'boundedWithinViewport': + group.overlay.minimumInViewportHeight = undefined; + group.overlay.minimumInViewportWidth = undefined; + break; + case undefined: + group.overlay.minimumInViewportHeight = + DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE; + group.overlay.minimumInViewportWidth = + DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE; + break; + default: + group.overlay.minimumInViewportHeight = + this.options.floatingGroupBounds?.minimumHeightWithinViewport; + group.overlay.minimumInViewportWidth = + this.options.floatingGroupBounds?.minimumWidthWithinViewport; + } + + group.overlay.setBounds({}); + } + } + this.layout(this.gridview.width, this.gridview.height, true); } diff --git a/packages/dockview-core/src/dockview/options.ts b/packages/dockview-core/src/dockview/options.ts index 23a06e0f5..920737a06 100644 --- a/packages/dockview-core/src/dockview/options.ts +++ b/packages/dockview-core/src/dockview/options.ts @@ -87,6 +87,12 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions { singleTabMode?: 'fullwidth' | 'default'; parentElement?: HTMLElement; disableFloatingGroups?: boolean; + floatingGroupBounds?: + | 'boundedWithinViewport' + | { + minimumHeightWithinViewport?: number; + minimumWidthWithinViewport?: number; + }; } export interface PanelOptions
{
diff --git a/packages/dockview/src/dockview/dockview.tsx b/packages/dockview/src/dockview/dockview.tsx
index 16effc460..7c6bcc435 100644
--- a/packages/dockview/src/dockview/dockview.tsx
+++ b/packages/dockview/src/dockview/dockview.tsx
@@ -69,6 +69,12 @@ export interface IDockviewReactProps {
leftHeaderActionsComponent?: React.FunctionComponent