mirror of
https://github.com/mathuo/dockview
synced 2025-01-23 01:45:58 +00:00
Merge pull request #431 from mathuo/430-support-droptarget-size-options
feat: expose dockview root droptarget overlay options (work-in-progress)
This commit is contained in:
commit
6c670c1fbb
@ -5,10 +5,6 @@ import { DragAndDropObserver } from './dnd';
|
|||||||
import { clamp } from '../math';
|
import { clamp } from '../math';
|
||||||
import { Direction } from '../gridview/baseComponentGridview';
|
import { Direction } from '../gridview/baseComponentGridview';
|
||||||
|
|
||||||
function numberOrFallback(maybeNumber: any, fallback: number): number {
|
|
||||||
return typeof maybeNumber === 'number' ? maybeNumber : fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function directionToPosition(direction: Direction): Position {
|
export function directionToPosition(direction: Direction): Position {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case 'above':
|
case 'above':
|
||||||
@ -54,6 +50,26 @@ export type CanDisplayOverlay =
|
|||||||
| boolean
|
| boolean
|
||||||
| ((dragEvent: DragEvent, state: Position) => 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 {
|
export class Droptarget extends CompositeDisposable {
|
||||||
private targetElement: HTMLElement | undefined;
|
private targetElement: HTMLElement | undefined;
|
||||||
private overlayElement: HTMLElement | undefined;
|
private overlayElement: HTMLElement | undefined;
|
||||||
@ -76,13 +92,7 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
private readonly options: {
|
private readonly options: {
|
||||||
canDisplayOverlay: CanDisplayOverlay;
|
canDisplayOverlay: CanDisplayOverlay;
|
||||||
acceptedTargetZones: Position[];
|
acceptedTargetZones: Position[];
|
||||||
overlayModel?: {
|
overlayModel?: DroptargetOverlayModel;
|
||||||
size?: { value: number; type: 'pixels' | 'percentage' };
|
|
||||||
activationSize?: {
|
|
||||||
value: number;
|
|
||||||
type: 'pixels' | 'percentage';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
@ -158,7 +168,7 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
|
|
||||||
this.toggleClasses(quadrant, width, height);
|
this.toggleClasses(quadrant, width, height);
|
||||||
|
|
||||||
this.setState(quadrant);
|
this._state = quadrant;
|
||||||
},
|
},
|
||||||
onDragLeave: () => {
|
onDragLeave: () => {
|
||||||
this.removeDropTarget();
|
this.removeDropTarget();
|
||||||
@ -189,6 +199,10 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
this._acceptedTargetZonesSet = new Set(acceptedTargetZones);
|
this._acceptedTargetZonesSet = new Set(acceptedTargetZones);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setOverlayModel(model: DroptargetOverlayModel): void {
|
||||||
|
this.options.overlayModel = model;
|
||||||
|
}
|
||||||
|
|
||||||
dispose(): void {
|
dispose(): void {
|
||||||
this.removeDropTarget();
|
this.removeDropTarget();
|
||||||
super.dispose();
|
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 {
|
private isAlreadyUsed(event: DragEvent): boolean {
|
||||||
const value = (event as any)[Droptarget.USED_EVENT_ID];
|
const value = (event as any)[Droptarget.USED_EVENT_ID];
|
||||||
@ -218,8 +232,8 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSmallX = width < 100;
|
const isSmallX = width < SMALL_WIDTH_BOUNDARY;
|
||||||
const isSmallY = height < 100;
|
const isSmallY = height < SMALL_HEIGHT_BOUNDARY;
|
||||||
|
|
||||||
const isLeft = quadrant === 'left';
|
const isLeft = quadrant === 'left';
|
||||||
const isRight = quadrant === 'right';
|
const isRight = quadrant === 'right';
|
||||||
@ -231,22 +245,18 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
const topClass = !isSmallY && isTop;
|
const topClass = !isSmallY && isTop;
|
||||||
const bottomClass = !isSmallY && isBottom;
|
const bottomClass = !isSmallY && isBottom;
|
||||||
|
|
||||||
let size = 0.5;
|
let size = 1;
|
||||||
|
|
||||||
if (this.options.overlayModel?.size?.type === 'percentage') {
|
const sizeOptions = this.options.overlayModel?.size ?? DEFAULT_SIZE;
|
||||||
size = clamp(this.options.overlayModel.size.value, 0, 100) / 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.overlayModel?.size?.type === 'pixels') {
|
if (sizeOptions.type === 'percentage') {
|
||||||
|
size = clamp(sizeOptions.value, 0, 100) / 100;
|
||||||
|
} else {
|
||||||
if (rightClass || leftClass) {
|
if (rightClass || leftClass) {
|
||||||
size =
|
size = clamp(0, sizeOptions.value, width) / width;
|
||||||
clamp(0, this.options.overlayModel.size.value, width) /
|
|
||||||
width;
|
|
||||||
}
|
}
|
||||||
if (topClass || bottomClass) {
|
if (topClass || bottomClass) {
|
||||||
size =
|
size = clamp(0, sizeOptions.value, height) / height;
|
||||||
clamp(0, this.options.overlayModel.size.value, height) /
|
|
||||||
height;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,26 +291,6 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
toggleClass(this.overlayElement, 'dv-overlay-bottom', isBottom);
|
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(
|
private calculateQuadrant(
|
||||||
overlayType: Set<Position>,
|
overlayType: Set<Position>,
|
||||||
x: number,
|
x: number,
|
||||||
@ -308,14 +298,11 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
width: number,
|
width: number,
|
||||||
height: number
|
height: number
|
||||||
): Position | null {
|
): Position | null {
|
||||||
const isPercentage =
|
const activationSizeOptions =
|
||||||
this.options.overlayModel?.activationSize === undefined ||
|
this.options.overlayModel?.activationSize ??
|
||||||
this.options.overlayModel?.activationSize?.type === 'percentage';
|
DEFAULT_ACTIVATION_SIZE;
|
||||||
|
|
||||||
const value = numberOrFallback(
|
const isPercentage = activationSizeOptions.type === 'percentage';
|
||||||
this.options?.overlayModel?.activationSize?.value,
|
|
||||||
20
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isPercentage) {
|
if (isPercentage) {
|
||||||
return calculateQuadrantAsPercentage(
|
return calculateQuadrantAsPercentage(
|
||||||
@ -324,7 +311,7 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
y,
|
y,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
value
|
activationSizeOptions.value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +321,7 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
y,
|
y,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
value
|
activationSizeOptions.value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,12 @@ import {
|
|||||||
getGridLocation,
|
getGridLocation,
|
||||||
ISerializedLeafNode,
|
ISerializedLeafNode,
|
||||||
} from '../gridview/gridview';
|
} 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 { tail, sequenceEquals, remove } from '../array';
|
||||||
import { DockviewPanel, IDockviewPanel } from './dockviewPanel';
|
import { DockviewPanel, IDockviewPanel } from './dockviewPanel';
|
||||||
import { CompositeDisposable, Disposable } from '../lifecycle';
|
import { CompositeDisposable, Disposable } from '../lifecycle';
|
||||||
@ -63,6 +68,11 @@ import {
|
|||||||
OverlayRenderContainer,
|
OverlayRenderContainer,
|
||||||
} from '../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 getTheme(element: HTMLElement): string | undefined {
|
||||||
function toClassList(element: HTMLElement) {
|
function toClassList(element: HTMLElement) {
|
||||||
const list: string[] = [];
|
const list: string[] = [];
|
||||||
@ -219,6 +229,7 @@ export type DockviewComponentUpdateOptions = Pick<
|
|||||||
| 'createPrefixHeaderActionsElement'
|
| 'createPrefixHeaderActionsElement'
|
||||||
| 'disableFloatingGroups'
|
| 'disableFloatingGroups'
|
||||||
| 'floatingGroupBounds'
|
| 'floatingGroupBounds'
|
||||||
|
| 'rootOverlayModel'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export interface DockviewDropEvent extends GroupviewDropEvent {
|
export interface DockviewDropEvent extends GroupviewDropEvent {
|
||||||
@ -319,6 +330,7 @@ export class DockviewComponent
|
|||||||
|
|
||||||
private readonly _floatingGroups: DockviewFloatingGroupPanel[] = [];
|
private readonly _floatingGroups: DockviewFloatingGroupPanel[] = [];
|
||||||
private readonly _popoutGroups: DockviewPopoutGroupPanel[] = [];
|
private readonly _popoutGroups: DockviewPopoutGroupPanel[] = [];
|
||||||
|
private readonly _rootDropTarget: Droptarget;
|
||||||
|
|
||||||
get orientation(): Orientation {
|
get orientation(): Orientation {
|
||||||
return this.gridview.orientation;
|
return this.gridview.orientation;
|
||||||
@ -424,7 +436,7 @@ export class DockviewComponent
|
|||||||
this.options.watermarkComponent = Watermark;
|
this.options.watermarkComponent = Watermark;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dropTarget = new Droptarget(this.element, {
|
this._rootDropTarget = new Droptarget(this.element, {
|
||||||
canDisplayOverlay: (event, position) => {
|
canDisplayOverlay: (event, position) => {
|
||||||
const data = getPanelData();
|
const data = getPanelData();
|
||||||
|
|
||||||
@ -463,14 +475,12 @@ export class DockviewComponent
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
|
acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
|
||||||
overlayModel: {
|
overlayModel:
|
||||||
activationSize: { type: 'pixels', value: 10 },
|
this.options.rootOverlayModel ?? DEFAULT_ROOT_OVERLAY_MODEL,
|
||||||
size: { type: 'pixels', value: 20 },
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
dropTarget.onDrop((event) => {
|
this._rootDropTarget.onDrop((event) => {
|
||||||
const data = getPanelData();
|
const data = getPanelData();
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
@ -489,7 +499,7 @@ export class DockviewComponent
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
dropTarget
|
this._rootDropTarget
|
||||||
);
|
);
|
||||||
|
|
||||||
this._api = new DockviewApi(this);
|
this._api = new DockviewApi(this);
|
||||||
@ -720,20 +730,24 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateOptions(options: DockviewComponentUpdateOptions): void {
|
updateOptions(options: DockviewComponentUpdateOptions): void {
|
||||||
const hasOrientationChanged =
|
const changed_orientation =
|
||||||
typeof options.orientation === 'string' &&
|
typeof options.orientation === 'string' &&
|
||||||
this.gridview.orientation !== options.orientation;
|
this.gridview.orientation !== options.orientation;
|
||||||
const hasFloatingGroupOptionsChanged =
|
const changed_floatingGroupBounds =
|
||||||
options.floatingGroupBounds !== undefined &&
|
options.floatingGroupBounds !== undefined &&
|
||||||
options.floatingGroupBounds !== this.options.floatingGroupBounds;
|
options.floatingGroupBounds !== this.options.floatingGroupBounds;
|
||||||
|
|
||||||
|
const changed_rootOverlayOptions =
|
||||||
|
options.rootOverlayModel !== undefined &&
|
||||||
|
options.rootOverlayModel !== this.options.rootOverlayModel;
|
||||||
|
|
||||||
this._options = { ...this.options, ...options };
|
this._options = { ...this.options, ...options };
|
||||||
|
|
||||||
if (hasOrientationChanged) {
|
if (changed_orientation) {
|
||||||
this.gridview.orientation = options.orientation!;
|
this.gridview.orientation = options.orientation!;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasFloatingGroupOptionsChanged) {
|
if (changed_floatingGroupBounds) {
|
||||||
for (const group of this._floatingGroups) {
|
for (const group of this._floatingGroups) {
|
||||||
switch (this.options.floatingGroupBounds) {
|
switch (this.options.floatingGroupBounds) {
|
||||||
case 'boundedWithinViewport':
|
case 'boundedWithinViewport':
|
||||||
@ -757,6 +771,10 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changed_rootOverlayOptions) {
|
||||||
|
this._rootDropTarget.setOverlayModel(options.rootOverlayModel!);
|
||||||
|
}
|
||||||
|
|
||||||
this.layout(this.gridview.width, this.gridview.height, true);
|
this.layout(this.gridview.width, this.gridview.height, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import { DockviewGroupPanel } from './dockviewGroupPanel';
|
|||||||
import { ISplitviewStyles, Orientation } from '../splitview/splitview';
|
import { ISplitviewStyles, Orientation } from '../splitview/splitview';
|
||||||
import { PanelTransfer } from '../dnd/dataTransfer';
|
import { PanelTransfer } from '../dnd/dataTransfer';
|
||||||
import { IDisposable } from '../lifecycle';
|
import { IDisposable } from '../lifecycle';
|
||||||
import { Position } from '../dnd/droptarget';
|
import { DroptargetOverlayModel, Position } from '../dnd/droptarget';
|
||||||
import { IDockviewPanel } from './dockviewPanel';
|
import { IDockviewPanel } from './dockviewPanel';
|
||||||
import {
|
import {
|
||||||
ComponentConstructor,
|
ComponentConstructor,
|
||||||
@ -100,6 +100,7 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions {
|
|||||||
popoutUrl?: string;
|
popoutUrl?: string;
|
||||||
defaultRenderer?: DockviewPanelRenderer;
|
defaultRenderer?: DockviewPanelRenderer;
|
||||||
debug?: boolean;
|
debug?: boolean;
|
||||||
|
rootOverlayModel?: DroptargetOverlayModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PanelOptions<P extends object = Parameters> {
|
export interface PanelOptions<P extends object = Parameters> {
|
||||||
|
@ -55,6 +55,8 @@ export {
|
|||||||
Position,
|
Position,
|
||||||
positionToDirection,
|
positionToDirection,
|
||||||
directionToPosition,
|
directionToPosition,
|
||||||
|
MeasuredValue,
|
||||||
|
DroptargetOverlayModel,
|
||||||
} from './dnd/droptarget';
|
} from './dnd/droptarget';
|
||||||
export {
|
export {
|
||||||
FocusEvent,
|
FocusEvent,
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
DockviewGroupPanel,
|
DockviewGroupPanel,
|
||||||
IHeaderActionsRenderer,
|
IHeaderActionsRenderer,
|
||||||
DockviewPanelRenderer,
|
DockviewPanelRenderer,
|
||||||
|
DroptargetOverlayModel,
|
||||||
} from 'dockview-core';
|
} from 'dockview-core';
|
||||||
import { ReactPanelContentPart } from './reactContentPart';
|
import { ReactPanelContentPart } from './reactContentPart';
|
||||||
import { ReactPanelHeaderPart } from './reactHeaderPart';
|
import { ReactPanelHeaderPart } from './reactHeaderPart';
|
||||||
@ -79,6 +80,7 @@ export interface IDockviewReactProps {
|
|||||||
};
|
};
|
||||||
debug?: boolean;
|
debug?: boolean;
|
||||||
defaultRenderer?: DockviewPanelRenderer;
|
defaultRenderer?: DockviewPanelRenderer;
|
||||||
|
rootOverlayModel?: DroptargetOverlayModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_REACT_TAB = 'props.defaultTabComponent';
|
const DEFAULT_REACT_TAB = 'props.defaultTabComponent';
|
||||||
@ -180,6 +182,7 @@ export const DockviewReact = React.forwardRef(
|
|||||||
floatingGroupBounds: props.floatingGroupBounds,
|
floatingGroupBounds: props.floatingGroupBounds,
|
||||||
defaultRenderer: props.defaultRenderer,
|
defaultRenderer: props.defaultRenderer,
|
||||||
debug: props.debug,
|
debug: props.debug,
|
||||||
|
rootOverlayModel: props.rootOverlayModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { clientWidth, clientHeight } = domRef.current;
|
const { clientWidth, clientHeight } = domRef.current;
|
||||||
@ -312,6 +315,15 @@ export const DockviewReact = React.forwardRef(
|
|||||||
});
|
});
|
||||||
}, [props.leftHeaderActionsComponent]);
|
}, [props.leftHeaderActionsComponent]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!dockviewRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dockviewRef.current.updateOptions({
|
||||||
|
rootOverlayModel: props.rootOverlayModel,
|
||||||
|
});
|
||||||
|
}, [props.rootOverlayModel]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!dockviewRef.current) {
|
if (!dockviewRef.current) {
|
||||||
return;
|
return;
|
||||||
|
Loading…
Reference in New Issue
Block a user