Skip to content

Commit

Permalink
Merge pull request #431 from mathuo/430-support-droptarget-size-options
Browse files Browse the repository at this point in the history
feat: expose dockview root droptarget overlay options (work-in-progress)
  • Loading branch information
mathuo authored Jan 18, 2024
2 parents e5334f0 + cc88096 commit 6c670c1
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 68 deletions.
97 changes: 42 additions & 55 deletions packages/dockview-core/src/dnd/droptarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ import { DragAndDropObserver } from './dnd';
import { clamp } from '../math';
import { Direction } from '../gridview/baseComponentGridview';

function numberOrFallback(maybeNumber: any, fallback: number): number {
return typeof maybeNumber === 'number' ? maybeNumber : fallback;
}

export function directionToPosition(direction: Direction): Position {
switch (direction) {
case 'above':
Expand Down Expand Up @@ -54,6 +50,26 @@ export type CanDisplayOverlay =
| boolean
| ((dragEvent: DragEvent, state: Position) => boolean);

export type MeasuredValue = { value: number; type: 'pixels' | 'percentage' };

export type DroptargetOverlayModel = {
size?: MeasuredValue;
activationSize?: MeasuredValue;
};

const DEFAULT_ACTIVATION_SIZE: MeasuredValue = {
value: 20,
type: 'percentage',
};

const DEFAULT_SIZE: MeasuredValue = {
value: 50,
type: 'percentage',
};

const SMALL_WIDTH_BOUNDARY = 100;
const SMALL_HEIGHT_BOUNDARY = 100;

export class Droptarget extends CompositeDisposable {
private targetElement: HTMLElement | undefined;
private overlayElement: HTMLElement | undefined;
Expand All @@ -76,13 +92,7 @@ export class Droptarget extends CompositeDisposable {
private readonly options: {
canDisplayOverlay: CanDisplayOverlay;
acceptedTargetZones: Position[];
overlayModel?: {
size?: { value: number; type: 'pixels' | 'percentage' };
activationSize?: {
value: number;
type: 'pixels' | 'percentage';
};
};
overlayModel?: DroptargetOverlayModel;
}
) {
super();
Expand Down Expand Up @@ -158,7 +168,7 @@ export class Droptarget extends CompositeDisposable {

this.toggleClasses(quadrant, width, height);

this.setState(quadrant);
this._state = quadrant;
},
onDragLeave: () => {
this.removeDropTarget();
Expand Down Expand Up @@ -189,6 +199,10 @@ export class Droptarget extends CompositeDisposable {
this._acceptedTargetZonesSet = new Set(acceptedTargetZones);
}

setOverlayModel(model: DroptargetOverlayModel): void {
this.options.overlayModel = model;
}

dispose(): void {
this.removeDropTarget();
super.dispose();
Expand All @@ -202,7 +216,7 @@ export class Droptarget extends CompositeDisposable {
}

/**
* Check is the event has already been used by another instance od DropTarget
* Check is the event has already been used by another instance of DropTarget
*/
private isAlreadyUsed(event: DragEvent): boolean {
const value = (event as any)[Droptarget.USED_EVENT_ID];
Expand All @@ -218,8 +232,8 @@ export class Droptarget extends CompositeDisposable {
return;
}

const isSmallX = width < 100;
const isSmallY = height < 100;
const isSmallX = width < SMALL_WIDTH_BOUNDARY;
const isSmallY = height < SMALL_HEIGHT_BOUNDARY;

const isLeft = quadrant === 'left';
const isRight = quadrant === 'right';
Expand All @@ -231,22 +245,18 @@ export class Droptarget extends CompositeDisposable {
const topClass = !isSmallY && isTop;
const bottomClass = !isSmallY && isBottom;

let size = 0.5;
let size = 1;

if (this.options.overlayModel?.size?.type === 'percentage') {
size = clamp(this.options.overlayModel.size.value, 0, 100) / 100;
}
const sizeOptions = this.options.overlayModel?.size ?? DEFAULT_SIZE;

if (this.options.overlayModel?.size?.type === 'pixels') {
if (sizeOptions.type === 'percentage') {
size = clamp(sizeOptions.value, 0, 100) / 100;
} else {
if (rightClass || leftClass) {
size =
clamp(0, this.options.overlayModel.size.value, width) /
width;
size = clamp(0, sizeOptions.value, width) / width;
}
if (topClass || bottomClass) {
size =
clamp(0, this.options.overlayModel.size.value, height) /
height;
size = clamp(0, sizeOptions.value, height) / height;
}
}

Expand Down Expand Up @@ -281,41 +291,18 @@ export class Droptarget extends CompositeDisposable {
toggleClass(this.overlayElement, 'dv-overlay-bottom', isBottom);
}

private setState(quadrant: Position): void {
switch (quadrant) {
case 'top':
this._state = 'top';
break;
case 'left':
this._state = 'left';
break;
case 'bottom':
this._state = 'bottom';
break;
case 'right':
this._state = 'right';
break;
case 'center':
this._state = 'center';
break;
}
}

private calculateQuadrant(
overlayType: Set<Position>,
x: number,
y: number,
width: number,
height: number
): Position | null {
const isPercentage =
this.options.overlayModel?.activationSize === undefined ||
this.options.overlayModel?.activationSize?.type === 'percentage';
const activationSizeOptions =
this.options.overlayModel?.activationSize ??
DEFAULT_ACTIVATION_SIZE;

const value = numberOrFallback(
this.options?.overlayModel?.activationSize?.value,
20
);
const isPercentage = activationSizeOptions.type === 'percentage';

if (isPercentage) {
return calculateQuadrantAsPercentage(
Expand All @@ -324,7 +311,7 @@ export class Droptarget extends CompositeDisposable {
y,
width,
height,
value
activationSizeOptions.value
);
}

Expand All @@ -334,7 +321,7 @@ export class Droptarget extends CompositeDisposable {
y,
width,
height,
value
activationSizeOptions.value
);
}

Expand Down
42 changes: 30 additions & 12 deletions packages/dockview-core/src/dockview/dockviewComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import {
getGridLocation,
ISerializedLeafNode,
} from '../gridview/gridview';
import { directionToPosition, Droptarget, Position } from '../dnd/droptarget';
import {
directionToPosition,
Droptarget,
DroptargetOverlayModel,
Position,
} from '../dnd/droptarget';
import { tail, sequenceEquals, remove } from '../array';
import { DockviewPanel, IDockviewPanel } from './dockviewPanel';
import { CompositeDisposable, Disposable } from '../lifecycle';
Expand Down Expand Up @@ -63,6 +68,11 @@ import {
OverlayRenderContainer,
} from '../overlayRenderContainer';

const DEFAULT_ROOT_OVERLAY_MODEL: DroptargetOverlayModel = {
activationSize: { type: 'pixels', value: 10 },
size: { type: 'pixels', value: 20 },
};

function getTheme(element: HTMLElement): string | undefined {
function toClassList(element: HTMLElement) {
const list: string[] = [];
Expand Down Expand Up @@ -219,6 +229,7 @@ export type DockviewComponentUpdateOptions = Pick<
| 'createPrefixHeaderActionsElement'
| 'disableFloatingGroups'
| 'floatingGroupBounds'
| 'rootOverlayModel'
>;

export interface DockviewDropEvent extends GroupviewDropEvent {
Expand Down Expand Up @@ -319,6 +330,7 @@ export class DockviewComponent

private readonly _floatingGroups: DockviewFloatingGroupPanel[] = [];
private readonly _popoutGroups: DockviewPopoutGroupPanel[] = [];
private readonly _rootDropTarget: Droptarget;

get orientation(): Orientation {
return this.gridview.orientation;
Expand Down Expand Up @@ -424,7 +436,7 @@ export class DockviewComponent
this.options.watermarkComponent = Watermark;
}

const dropTarget = new Droptarget(this.element, {
this._rootDropTarget = new Droptarget(this.element, {
canDisplayOverlay: (event, position) => {
const data = getPanelData();

Expand Down Expand Up @@ -463,14 +475,12 @@ export class DockviewComponent
return false;
},
acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
overlayModel: {
activationSize: { type: 'pixels', value: 10 },
size: { type: 'pixels', value: 20 },
},
overlayModel:
this.options.rootOverlayModel ?? DEFAULT_ROOT_OVERLAY_MODEL,
});

this.addDisposables(
dropTarget.onDrop((event) => {
this._rootDropTarget.onDrop((event) => {
const data = getPanelData();

if (data) {
Expand All @@ -489,7 +499,7 @@ export class DockviewComponent
});
}
}),
dropTarget
this._rootDropTarget
);

this._api = new DockviewApi(this);
Expand Down Expand Up @@ -720,20 +730,24 @@ export class DockviewComponent
}

updateOptions(options: DockviewComponentUpdateOptions): void {
const hasOrientationChanged =
const changed_orientation =
typeof options.orientation === 'string' &&
this.gridview.orientation !== options.orientation;
const hasFloatingGroupOptionsChanged =
const changed_floatingGroupBounds =
options.floatingGroupBounds !== undefined &&
options.floatingGroupBounds !== this.options.floatingGroupBounds;

const changed_rootOverlayOptions =
options.rootOverlayModel !== undefined &&
options.rootOverlayModel !== this.options.rootOverlayModel;

this._options = { ...this.options, ...options };

if (hasOrientationChanged) {
if (changed_orientation) {
this.gridview.orientation = options.orientation!;
}

if (hasFloatingGroupOptionsChanged) {
if (changed_floatingGroupBounds) {
for (const group of this._floatingGroups) {
switch (this.options.floatingGroupBounds) {
case 'boundedWithinViewport':
Expand All @@ -757,6 +771,10 @@ export class DockviewComponent
}
}

if (changed_rootOverlayOptions) {
this._rootDropTarget.setOverlayModel(options.rootOverlayModel!);
}

this.layout(this.gridview.width, this.gridview.height, true);
}

Expand Down
3 changes: 2 additions & 1 deletion packages/dockview-core/src/dockview/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { DockviewGroupPanel } from './dockviewGroupPanel';
import { ISplitviewStyles, Orientation } from '../splitview/splitview';
import { PanelTransfer } from '../dnd/dataTransfer';
import { IDisposable } from '../lifecycle';
import { Position } from '../dnd/droptarget';
import { DroptargetOverlayModel, Position } from '../dnd/droptarget';
import { IDockviewPanel } from './dockviewPanel';
import {
ComponentConstructor,
Expand Down Expand Up @@ -100,6 +100,7 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions {
popoutUrl?: string;
defaultRenderer?: DockviewPanelRenderer;
debug?: boolean;
rootOverlayModel?: DroptargetOverlayModel;
}

export interface PanelOptions<P extends object = Parameters> {
Expand Down
2 changes: 2 additions & 0 deletions packages/dockview-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export {
Position,
positionToDirection,
directionToPosition,
MeasuredValue,
DroptargetOverlayModel,
} from './dnd/droptarget';
export {
FocusEvent,
Expand Down
12 changes: 12 additions & 0 deletions packages/dockview/src/dockview/dockview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
DockviewGroupPanel,
IHeaderActionsRenderer,
DockviewPanelRenderer,
DroptargetOverlayModel,
} from 'dockview-core';
import { ReactPanelContentPart } from './reactContentPart';
import { ReactPanelHeaderPart } from './reactHeaderPart';
Expand Down Expand Up @@ -79,6 +80,7 @@ export interface IDockviewReactProps {
};
debug?: boolean;
defaultRenderer?: DockviewPanelRenderer;
rootOverlayModel?: DroptargetOverlayModel;
}

const DEFAULT_REACT_TAB = 'props.defaultTabComponent';
Expand Down Expand Up @@ -180,6 +182,7 @@ export const DockviewReact = React.forwardRef(
floatingGroupBounds: props.floatingGroupBounds,
defaultRenderer: props.defaultRenderer,
debug: props.debug,
rootOverlayModel: props.rootOverlayModel,
});

const { clientWidth, clientHeight } = domRef.current;
Expand Down Expand Up @@ -312,6 +315,15 @@ export const DockviewReact = React.forwardRef(
});
}, [props.leftHeaderActionsComponent]);

React.useEffect(() => {
if (!dockviewRef.current) {
return;
}
dockviewRef.current.updateOptions({
rootOverlayModel: props.rootOverlayModel,
});
}, [props.rootOverlayModel]);

React.useEffect(() => {
if (!dockviewRef.current) {
return;
Expand Down

0 comments on commit 6c670c1

Please sign in to comment.