From cc88096413056482708ba276bfb689feb53a1908 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Wed, 10 Jan 2024 10:26:58 +0000 Subject: [PATCH] feat: expose dockview root droptarget overlay options --- packages/dockview-core/src/dnd/droptarget.ts | 97 ++++++++----------- .../src/dockview/dockviewComponent.ts | 42 +++++--- .../dockview-core/src/dockview/options.ts | 3 +- packages/dockview-core/src/index.ts | 2 + packages/dockview/src/dockview/dockview.tsx | 12 +++ 5 files changed, 88 insertions(+), 68 deletions(-) diff --git a/packages/dockview-core/src/dnd/droptarget.ts b/packages/dockview-core/src/dnd/droptarget.ts index 357d4c32f..caa35d1e2 100644 --- a/packages/dockview-core/src/dnd/droptarget.ts +++ b/packages/dockview-core/src/dnd/droptarget.ts @@ -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': @@ -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; @@ -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(); @@ -158,7 +168,7 @@ export class Droptarget extends CompositeDisposable { this.toggleClasses(quadrant, width, height); - this.setState(quadrant); + this._state = quadrant; }, onDragLeave: () => { this.removeDropTarget(); @@ -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(); @@ -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]; @@ -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'; @@ -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; } } @@ -275,26 +285,6 @@ export class Droptarget extends CompositeDisposable { toggleClass(this.overlayElement, 'small-bottom', isSmallY && 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, x: number, @@ -302,14 +292,11 @@ export class Droptarget extends CompositeDisposable { 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( @@ -318,7 +305,7 @@ export class Droptarget extends CompositeDisposable { y, width, height, - value + activationSizeOptions.value ); } @@ -328,7 +315,7 @@ export class Droptarget extends CompositeDisposable { y, width, height, - value + activationSizeOptions.value ); } diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index 019b2789a..39e9126cf 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -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'; @@ -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[] = []; @@ -219,6 +229,7 @@ export type DockviewComponentUpdateOptions = Pick< | 'createPrefixHeaderActionsElement' | 'disableFloatingGroups' | 'floatingGroupBounds' + | 'rootOverlayModel' >; export interface DockviewDropEvent extends GroupviewDropEvent { @@ -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; @@ -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(); @@ -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) { @@ -489,7 +499,7 @@ export class DockviewComponent }); } }), - dropTarget + this._rootDropTarget ); this._api = new DockviewApi(this); @@ -714,20 +724,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': @@ -751,6 +765,10 @@ export class DockviewComponent } } + if (changed_rootOverlayOptions) { + this._rootDropTarget.setOverlayModel(options.rootOverlayModel!); + } + 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 96493354b..34ab98989 100644 --- a/packages/dockview-core/src/dockview/options.ts +++ b/packages/dockview-core/src/dockview/options.ts @@ -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, @@ -100,6 +100,7 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions { popoutUrl?: string; defaultRenderer?: DockviewPanelRenderer; debug?: boolean; + rootOverlayModel?: DroptargetOverlayModel; } export interface PanelOptions

{ diff --git a/packages/dockview-core/src/index.ts b/packages/dockview-core/src/index.ts index b3fda3fa7..62d415174 100644 --- a/packages/dockview-core/src/index.ts +++ b/packages/dockview-core/src/index.ts @@ -55,6 +55,8 @@ export { Position, positionToDirection, directionToPosition, + MeasuredValue, + DroptargetOverlayModel, } from './dnd/droptarget'; export { FocusEvent, diff --git a/packages/dockview/src/dockview/dockview.tsx b/packages/dockview/src/dockview/dockview.tsx index 3a7c2ac35..91aaa0562 100644 --- a/packages/dockview/src/dockview/dockview.tsx +++ b/packages/dockview/src/dockview/dockview.tsx @@ -11,6 +11,7 @@ import { DockviewGroupPanel, IHeaderActionsRenderer, DockviewPanelRenderer, + DroptargetOverlayModel, } from 'dockview-core'; import { ReactPanelContentPart } from './reactContentPart'; import { ReactPanelHeaderPart } from './reactHeaderPart'; @@ -79,6 +80,7 @@ export interface IDockviewReactProps { }; debug?: boolean; defaultRenderer?: DockviewPanelRenderer; + rootOverlayModel?: DroptargetOverlayModel; } const DEFAULT_REACT_TAB = 'props.defaultTabComponent'; @@ -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; @@ -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;