feat: floating group overlay rules

This commit is contained in:
mathuo 2023-09-04 19:18:19 +01:00
parent f6cfb4418c
commit 292e25935f
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
6 changed files with 83 additions and 15 deletions

View File

@ -40,6 +40,14 @@ export class Overlay extends CompositeDisposable {
private static MINIMUM_HEIGHT = 20; private static MINIMUM_HEIGHT = 20;
private static MINIMUM_WIDTH = 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( constructor(
private readonly options: { private readonly options: {
height: number; height: number;
@ -109,7 +117,10 @@ export class Overlay extends CompositeDisposable {
const xOffset = Math.max(0, this.getMinimumWidth(overlayRect.width)); const xOffset = Math.max(0, this.getMinimumWidth(overlayRect.width));
// a minimum height of minimumViewportHeight must be inside the viewport // a minimum height of minimumViewportHeight must be inside the viewport
const yOffset = Math.max(0, this.getMinimumHeight(overlayRect.height)); const yOffset =
typeof this.options.minimumInViewportHeight === 'number'
? Math.max(0, this.getMinimumHeight(overlayRect.height))
: 0;
const left = clamp( const left = clamp(
overlayRect.left - containerRect.left, overlayRect.left - containerRect.left,
@ -376,7 +387,9 @@ export class Overlay extends CompositeDisposable {
height = clamp( height = clamp(
y - top, y - top,
top < 0 && this.options.minimumInViewportHeight top < 0 &&
typeof this.options
.minimumInViewportHeight === 'number'
? -top + ? -top +
this.options.minimumInViewportHeight this.options.minimumInViewportHeight
: Overlay.MINIMUM_HEIGHT, : Overlay.MINIMUM_HEIGHT,
@ -413,7 +426,9 @@ export class Overlay extends CompositeDisposable {
width = clamp( width = clamp(
x - left, x - left,
left < 0 && this.options.minimumInViewportWidth left < 0 &&
typeof this.options
.minimumInViewportWidth === 'number'
? -left + ? -left +
this.options.minimumInViewportWidth this.options.minimumInViewportWidth
: Overlay.MINIMUM_WIDTH, : Overlay.MINIMUM_WIDTH,

View File

@ -52,6 +52,8 @@ import {
IDockviewFloatingGroupPanel, IDockviewFloatingGroupPanel,
} from './dockviewFloatingGroupPanel'; } from './dockviewFloatingGroupPanel';
const DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE = 100;
export interface PanelReference { export interface PanelReference {
update: (event: { params: { [key: string]: any } }) => void; update: (event: { params: { [key: string]: any } }) => void;
remove: () => void; remove: () => void;
@ -87,7 +89,7 @@ export type DockviewComponentUpdateOptions = Pick<
| 'createLeftHeaderActionsElement' | 'createLeftHeaderActionsElement'
| 'createRightHeaderActionsElement' | 'createRightHeaderActionsElement'
| 'disableFloatingGroups' | 'disableFloatingGroups'
| 'floatingGroupsPosition' | 'floatingGroupBounds'
>; >;
export interface DockviewDropEvent extends GroupviewDropEvent { export interface DockviewDropEvent extends GroupviewDropEvent {
@ -361,15 +363,17 @@ export class DockviewComponent
left: overlayLeft, left: overlayLeft,
top: overlayTop, top: overlayTop,
minimumInViewportWidth: minimumInViewportWidth:
this.options.floatingGroupsPosition === 'boundedWithinViewport' this.options.floatingGroupBounds === 'boundedWithinViewport'
? undefined ? undefined
: this.options.floatingGroupsPosition : this.options.floatingGroupBounds
?.minimumWidthWithinViewport ?? 100, ?.minimumWidthWithinViewport ??
DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE,
minimumInViewportHeight: minimumInViewportHeight:
this.options.floatingGroupsPosition === 'boundedWithinViewport' this.options.floatingGroupBounds === 'boundedWithinViewport'
? undefined ? undefined
: this.options.floatingGroupsPosition : this.options.floatingGroupBounds
?.minimumHeightWithinViewport ?? 100, ?.minimumHeightWithinViewport ??
DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE,
}); });
const el = group.element.querySelector('.void-container'); const el = group.element.querySelector('.void-container');
@ -465,6 +469,9 @@ export class DockviewComponent
const hasOrientationChanged = const hasOrientationChanged =
typeof options.orientation === 'string' && typeof options.orientation === 'string' &&
this.gridview.orientation !== options.orientation; this.gridview.orientation !== options.orientation;
const hasFloatingGroupOptionsChanged =
typeof options.floatingGroupBounds !== undefined &&
options.floatingGroupBounds !== this.options.floatingGroupBounds;
this._options = { ...this.options, ...options }; this._options = { ...this.options, ...options };
@ -472,6 +479,30 @@ export class DockviewComponent
this.gridview.orientation = options.orientation!; 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); this.layout(this.gridview.width, this.gridview.height, true);
} }

View File

@ -87,7 +87,7 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions {
singleTabMode?: 'fullwidth' | 'default'; singleTabMode?: 'fullwidth' | 'default';
parentElement?: HTMLElement; parentElement?: HTMLElement;
disableFloatingGroups?: boolean; disableFloatingGroups?: boolean;
floatingGroupsPosition?: floatingGroupBounds?:
| 'boundedWithinViewport' | 'boundedWithinViewport'
| { | {
minimumHeightWithinViewport?: number; minimumHeightWithinViewport?: number;

View File

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

View File

@ -401,6 +401,12 @@ Floating groups can be interacted with whilst holding the `shift` key activating
Floating groups can be programatically added through the dockview `api` method `api.addFloatingGroup(...)` and you can check whether Floating groups can be programatically added through the dockview `api` method `api.addFloatingGroup(...)` and you can check whether
a group is floating via the `group.api.isFloating` property. See examples for full code. a group is floating via the `group.api.isFloating` property. See examples for full code.
You can control the bounding box of floating groups through the optional `floatingGroupBounds` options:
- `boundedWithinViewport` will force the entire floating group to be bounded within the docks viewport.
- `{minimumHeightWithinViewport?: number, minimumWidthWithinViewport?: number}` sets the respective dimension minimums that must appears within the docks viewport
- If no options are provided the defaults of `100px` minimum height and width within the viewport are set.
<MultiFrameworkContainer <MultiFrameworkContainer
height={600} height={600}
sandboxId="floatinggroup-dockview" sandboxId="floatinggroup-dockview"

View File

@ -155,6 +155,10 @@ export const DockviewPersistance = (props: { theme?: string }) => {
setApi(event.api); setApi(event.api);
}; };
const [options, setOptions] = React.useState<
'boundedWithinViewport' | undefined
>(undefined);
return ( return (
<div <div
style={{ style={{
@ -197,6 +201,17 @@ export const DockviewPersistance = (props: { theme?: string }) => {
> >
Add Floating Group Add Floating Group
</button> </button>
<button
onClick={() => {
setOptions(
options === undefined
? 'boundedWithinViewport'
: undefined
);
}}
>
{`Bounds: ${options ? 'Within' : 'Overflow'}`}
</button>
<button <button
onClick={() => { onClick={() => {
setDisableFloatingGroups((x) => !x); setDisableFloatingGroups((x) => !x);
@ -219,6 +234,7 @@ export const DockviewPersistance = (props: { theme?: string }) => {
leftHeaderActionsComponent={LeftComponent} leftHeaderActionsComponent={LeftComponent}
rightHeaderActionsComponent={RightComponent} rightHeaderActionsComponent={RightComponent}
disableFloatingGroups={disableFloatingGroups} disableFloatingGroups={disableFloatingGroups}
floatingGroupBounds={options}
className={`${props.theme || 'dockview-theme-abyss'}`} className={`${props.theme || 'dockview-theme-abyss'}`}
/> />
</div> </div>