feat: expose onWillDrop

This commit is contained in:
mathuo 2024-01-18 21:57:11 +00:00
parent 8da26f1484
commit 6a1f47d4da
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
10 changed files with 413 additions and 136 deletions

View File

@ -1,5 +1,4 @@
import {
DockviewDropEvent,
IDockviewComponent,
SerializedDockview,
} from '../dockview/dockviewComponent';
@ -43,6 +42,11 @@ import {
TabDragEvent,
} from '../dockview/components/titlebar/tabsContainer';
import { Box } from '../types';
import {
DockviewDidDropEvent,
DockviewWillDropEvent,
WillShowOverlayLocationEvent,
} from '../dockview/dockviewGroupPanelModel';
export interface CommonApi<T = any> {
readonly height: number;
@ -648,10 +652,27 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
/**
* Invoked when a Drag'n'Drop event occurs that the component was unable to handle. Exposed for custom Drag'n'Drop functionality.
*/
get onDidDrop(): Event<DockviewDropEvent> {
get onDidDrop(): Event<DockviewDidDropEvent> {
return this.component.onDidDrop;
}
/**
* Invoked when a Drag'n'Drop event occurs but before dockview handles it giving the user an opportunity to intecept and
* prevent the event from occuring using the standard `preventDefault()` syntax.
*
* Preventing certain events may causes unexpected behaviours, use carefully.
*/
get onWillDrop(): Event<DockviewWillDropEvent> {
return this.component.onWillDrop;
}
/**
*
*/
get onWillShowOverlay(): Event<WillShowOverlayLocationEvent> {
return this.component.onWillShowOverlay;
}
/**
* Invoked before a group is dragged. Exposed for custom Drag'n'Drop functionality.
*/

View File

@ -1,10 +1,37 @@
import { toggleClass } from '../dom';
import { Emitter, Event } from '../events';
import { DockviewEvent, Emitter, Event } from '../events';
import { CompositeDisposable } from '../lifecycle';
import { DragAndDropObserver } from './dnd';
import { clamp } from '../math';
import { Direction } from '../gridview/baseComponentGridview';
export interface DroptargetEvent {
readonly position: Position;
readonly nativeEvent: DragEvent;
}
export class WillShowOverlayEvent
extends DockviewEvent
implements DroptargetEvent
{
get nativeEvent(): DragEvent {
return this.options.nativeEvent;
}
get position(): Position {
return this.options.position;
}
constructor(
private readonly options: {
nativeEvent: DragEvent;
position: Position;
}
) {
super();
}
}
export function directionToPosition(direction: Direction): Position {
switch (direction) {
case 'above':
@ -39,11 +66,6 @@ export function positionToDirection(position: Position): Direction {
}
}
export interface DroptargetEvent {
readonly position: Position;
readonly nativeEvent: DragEvent;
}
export type Position = 'top' | 'bottom' | 'left' | 'right' | 'center';
export type CanDisplayOverlay =
@ -70,6 +92,12 @@ const DEFAULT_SIZE: MeasuredValue = {
const SMALL_WIDTH_BOUNDARY = 100;
const SMALL_HEIGHT_BOUNDARY = 100;
export interface DroptargetOptions {
canDisplayOverlay: CanDisplayOverlay;
acceptedTargetZones: Position[];
overlayModel?: DroptargetOverlayModel;
}
export class Droptarget extends CompositeDisposable {
private targetElement: HTMLElement | undefined;
private overlayElement: HTMLElement | undefined;
@ -79,6 +107,10 @@ export class Droptarget extends CompositeDisposable {
private readonly _onDrop = new Emitter<DroptargetEvent>();
readonly onDrop: Event<DroptargetEvent> = this._onDrop.event;
private readonly _onWillShowOverlay = new Emitter<WillShowOverlayEvent>();
readonly onWillShowOverlay: Event<WillShowOverlayEvent> =
this._onWillShowOverlay.event;
readonly dnd: DragAndDropObserver;
private static USED_EVENT_ID = '__dockview_droptarget_event_is_used__';
@ -89,11 +121,7 @@ export class Droptarget extends CompositeDisposable {
constructor(
private readonly element: HTMLElement,
private readonly options: {
canDisplayOverlay: CanDisplayOverlay;
acceptedTargetZones: Position[];
overlayModel?: DroptargetOverlayModel;
}
private readonly options: DroptargetOptions
) {
super();
@ -142,6 +170,22 @@ export class Droptarget extends CompositeDisposable {
return;
}
const willShowOverlayEvent = new WillShowOverlayEvent({
nativeEvent: e,
position: quadrant,
});
/**
* Provide an opportunity to prevent the overlay appearing and in turn
* any dnd behaviours
*/
this._onWillShowOverlay.fire(willShowOverlayEvent);
if (willShowOverlayEvent.defaultPrevented) {
this.removeDropTarget();
return;
}
if (typeof this.options.canDisplayOverlay === 'boolean') {
if (!this.options.canDisplayOverlay) {
this.removeDropTarget();
@ -192,7 +236,7 @@ export class Droptarget extends CompositeDisposable {
},
});
this.addDisposables(this._onDrop, this.dnd);
this.addDisposables(this._onDrop, this._onWillShowOverlay, this.dnd);
}
setTargetZones(acceptedTargetZones: Position[]): void {

View File

@ -9,7 +9,12 @@ import { toggleClass } from '../../../dom';
import { DockviewComponent } from '../../dockviewComponent';
import { ITabRenderer } from '../../types';
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
import { DroptargetEvent, Droptarget } from '../../../dnd/droptarget';
import {
DroptargetEvent,
Droptarget,
Position,
WillShowOverlayEvent,
} from '../../../dnd/droptarget';
import { DragHandler } from '../../../dnd/abstractDragHandler';
import { IDockviewPanel } from '../../dockviewPanel';
@ -40,18 +45,9 @@ class TabDragHandler extends DragHandler {
}
}
export interface ITab extends IDisposable {
readonly panel: IDockviewPanel;
readonly element: HTMLElement;
setContent: (element: ITabRenderer) => void;
onChanged: Event<MouseEvent>;
onDrop: Event<DroptargetEvent>;
setActive(isActive: boolean): void;
}
export class Tab extends CompositeDisposable implements ITab {
export class Tab extends CompositeDisposable {
private readonly _element: HTMLElement;
private readonly droptarget: Droptarget;
private readonly dropTarget: Droptarget;
private content: ITabRenderer | undefined = undefined;
private readonly _onChanged = new Emitter<MouseEvent>();
@ -63,6 +59,8 @@ export class Tab extends CompositeDisposable implements ITab {
private readonly _onDragStart = new Emitter<DragEvent>();
readonly onDragStart = this._onDragStart.event;
readonly onWillShowOverlay: Event<WillShowOverlayEvent>;
public get element(): HTMLElement {
return this._element;
}
@ -88,7 +86,7 @@ export class Tab extends CompositeDisposable implements ITab {
this.panel
);
this.droptarget = new Droptarget(this._element, {
this.dropTarget = new Droptarget(this._element, {
acceptedTargetZones: ['center'],
canDisplayOverlay: (event, position) => {
if (this.group.locked) {
@ -117,6 +115,8 @@ export class Tab extends CompositeDisposable implements ITab {
},
});
this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
this.addDisposables(
this._onChanged,
this._onDropped,
@ -132,10 +132,10 @@ export class Tab extends CompositeDisposable implements ITab {
this._onChanged.fire(event);
}),
this.droptarget.onDrop((event) => {
this.dropTarget.onDrop((event) => {
this._onDropped.fire(event);
}),
this.droptarget
this.dropTarget
);
}

View File

@ -4,12 +4,14 @@ import {
IValueDisposable,
} from '../../../lifecycle';
import { addDisposableListener, Emitter, Event } from '../../../events';
import { ITab, Tab } from '../tab/tab';
import { DockviewComponent } from '../../dockviewComponent';
import { Tab } from '../tab/tab';
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
import { VoidContainer } from './voidContainer';
import { toggleClass } from '../../../dom';
import { DockviewPanel, IDockviewPanel } from '../../dockviewPanel';
import { DockviewComponent } from '../../dockviewComponent';
import { WillShowOverlayEvent } from '../../../dnd/droptarget';
import { DockviewGroupDropLocation } from '../../dockviewGroupPanelModel';
export interface TabDropIndexEvent {
readonly event: DragEvent;
@ -30,17 +32,21 @@ export interface ITabsContainer extends IDisposable {
readonly element: HTMLElement;
readonly panels: string[];
readonly size: number;
readonly onDrop: Event<TabDropIndexEvent>;
readonly onTabDragStart: Event<TabDragEvent>;
readonly onGroupDragStart: Event<GroupDragEvent>;
readonly onWillShowOverlay: Event<{
event: WillShowOverlayEvent;
kind: DockviewGroupDropLocation;
}>;
hidden: boolean;
delete: (id: string) => void;
indexOf: (id: string) => number;
onDrop: Event<TabDropIndexEvent>;
onTabDragStart: Event<TabDragEvent>;
onGroupDragStart: Event<GroupDragEvent>;
setActive: (isGroupActive: boolean) => void;
setActivePanel: (panel: IDockviewPanel) => void;
isActive: (tab: ITab) => boolean;
closePanel: (panel: IDockviewPanel) => void;
openPanel: (panel: IDockviewPanel, index?: number) => void;
delete(id: string): void;
indexOf(id: string): number;
setActive(isGroupActive: boolean): void;
setActivePanel(panel: IDockviewPanel): void;
isActive(tab: Tab): boolean;
closePanel(panel: IDockviewPanel): void;
openPanel(panel: IDockviewPanel, index?: number): void;
setRightActionsElement(element: HTMLElement | undefined): void;
setLeftActionsElement(element: HTMLElement | undefined): void;
setPrefixActionsElement(element: HTMLElement | undefined): void;
@ -59,7 +65,7 @@ export class TabsContainer
private readonly preActionsContainer: HTMLElement;
private readonly voidContainer: VoidContainer;
private tabs: IValueDisposable<ITab>[] = [];
private tabs: IValueDisposable<Tab>[] = [];
private selectedIndex = -1;
private rightActions: HTMLElement | undefined;
private leftActions: HTMLElement | undefined;
@ -77,6 +83,15 @@ export class TabsContainer
readonly onGroupDragStart: Event<GroupDragEvent> =
this._onGroupDragStart.event;
private readonly _onWillShowOverlay = new Emitter<{
event: WillShowOverlayEvent;
kind: DockviewGroupDropLocation;
}>();
readonly onWillShowOverlay: Event<{
event: WillShowOverlayEvent;
kind: DockviewGroupDropLocation;
}> = this._onWillShowOverlay.event;
get panels(): string[] {
return this.tabs.map((_) => _.value.panel.id);
}
@ -150,7 +165,7 @@ export class TabsContainer
return this._element;
}
public isActive(tab: ITab): boolean {
public isActive(tab: Tab): boolean {
return (
this.selectedIndex > -1 &&
this.tabs[this.selectedIndex].value === tab
@ -167,12 +182,6 @@ export class TabsContainer
) {
super();
this.addDisposables(
this._onDrop,
this._onTabDragStart,
this._onGroupDragStart
);
this._element = document.createElement('div');
this._element.className = 'tabs-and-actions-container';
@ -182,27 +191,6 @@ export class TabsContainer
this.accessor.options.singleTabMode === 'fullwidth'
);
this.addDisposables(
this.accessor.onDidAddPanel((e) => {
if (e.api.group === this.group) {
toggleClass(
this._element,
'dv-single-tab',
this.size === 1
);
}
}),
this.accessor.onDidRemovePanel((e) => {
if (e.api.group === this.group) {
toggleClass(
this._element,
'dv-single-tab',
this.size === 1
);
}
})
);
this.rightActionsContainer = document.createElement('div');
this.rightActionsContainer.className = 'right-actions-container';
@ -224,6 +212,28 @@ export class TabsContainer
this._element.appendChild(this.rightActionsContainer);
this.addDisposables(
this.accessor.onDidAddPanel((e) => {
if (e.api.group === this.group) {
toggleClass(
this._element,
'dv-single-tab',
this.size === 1
);
}
}),
this.accessor.onDidRemovePanel((e) => {
if (e.api.group === this.group) {
toggleClass(
this._element,
'dv-single-tab',
this.size === 1
);
}
}),
this._onWillShowOverlay,
this._onDrop,
this._onTabDragStart,
this._onGroupDragStart,
this.voidContainer,
this.voidContainer.onDragStart((event) => {
this._onGroupDragStart.fire({
@ -237,6 +247,9 @@ export class TabsContainer
index: this.tabs.length,
});
}),
this.voidContainer.onWillShowOverlay((event) => {
this._onWillShowOverlay.fire({ event, kind: 'header_space' });
}),
addDisposableListener(
this.voidContainer.element,
'mousedown',
@ -286,7 +299,7 @@ export class TabsContainer
}
private addTab(
tab: IValueDisposable<ITab>,
tab: IValueDisposable<Tab>,
index: number = this.tabs.length
): void {
if (index < 0 || index > this.tabs.length) {
@ -395,10 +408,13 @@ export class TabsContainer
event: event.nativeEvent,
index: this.tabs.findIndex((x) => x.value === tab),
});
}),
tab.onWillShowOverlay((event) => {
this._onWillShowOverlay.fire({ event, kind: 'tab' });
})
);
const value: IValueDisposable<ITab> = { value: tab, disposable };
const value: IValueDisposable<Tab> = { value: tab, disposable };
this.addTab(value, index);
}

View File

@ -1,6 +1,10 @@
import { last } from '../../../array';
import { getPanelData } from '../../../dnd/dataTransfer';
import { Droptarget, DroptargetEvent } from '../../../dnd/droptarget';
import {
Droptarget,
DroptargetEvent,
WillShowOverlayEvent,
} from '../../../dnd/droptarget';
import { GroupDragHandler } from '../../../dnd/groupDragHandler';
import { DockviewComponent } from '../../dockviewComponent';
import { addDisposableListener, Emitter, Event } from '../../../events';
@ -9,7 +13,7 @@ import { DockviewGroupPanel } from '../../dockviewGroupPanel';
export class VoidContainer extends CompositeDisposable {
private readonly _element: HTMLElement;
private readonly voidDropTarget: Droptarget;
private readonly dropTraget: Droptarget;
private readonly _onDrop = new Emitter<DroptargetEvent>();
readonly onDrop: Event<DroptargetEvent> = this._onDrop.event;
@ -17,6 +21,8 @@ export class VoidContainer extends CompositeDisposable {
private readonly _onDragStart = new Emitter<DragEvent>();
readonly onDragStart = this._onDragStart.event;
readonly onWillShowOverlay: Event<WillShowOverlayEvent>;
get element(): HTMLElement {
return this._element;
}
@ -43,7 +49,7 @@ export class VoidContainer extends CompositeDisposable {
const handler = new GroupDragHandler(this._element, accessor, group);
this.voidDropTarget = new Droptarget(this._element, {
this.dropTraget = new Droptarget(this._element, {
acceptedTargetZones: ['center'],
canDisplayOverlay: (event, position) => {
const data = getPanelData();
@ -65,15 +71,17 @@ export class VoidContainer extends CompositeDisposable {
},
});
this.onWillShowOverlay = this.dropTraget.onWillShowOverlay;
this.addDisposables(
handler,
handler.onDragStart((event) => {
this._onDragStart.fire(event);
}),
this.voidDropTarget.onDrop((event) => {
this.dropTraget.onDrop((event) => {
this._onDrop.fire(event);
}),
this.voidDropTarget
this.dropTraget
);
}
}

View File

@ -40,7 +40,9 @@ import { Orientation, Sizing } from '../splitview/splitview';
import {
GroupOptions,
GroupPanelViewState,
GroupviewDropEvent,
DockviewDidDropEvent,
DockviewWillDropEvent,
WillShowOverlayLocationEvent,
} from './dockviewGroupPanelModel';
import { DockviewGroupPanel } from './dockviewGroupPanel';
import { DockviewPanelModel } from './dockviewPanelModel';
@ -226,18 +228,16 @@ export type DockviewComponentUpdateOptions = Pick<
| 'disableFloatingGroups'
| 'floatingGroupBounds'
| 'rootOverlayModel'
| 'disableDnd'
>;
export interface DockviewDropEvent extends GroupviewDropEvent {
api: DockviewApi;
group: DockviewGroupPanel | null;
}
export interface IDockviewComponent extends IBaseGrid<DockviewGroupPanel> {
readonly activePanel: IDockviewPanel | undefined;
readonly totalPanels: number;
readonly panels: IDockviewPanel[];
readonly onDidDrop: Event<DockviewDropEvent>;
readonly onDidDrop: Event<DockviewDidDropEvent>;
readonly onWillDrop: Event<DockviewWillDropEvent>;
readonly onWillShowOverlay: Event<WillShowOverlayLocationEvent>;
readonly orientation: Orientation;
updateOptions(options: DockviewComponentUpdateOptions): void;
moveGroupOrPanel(
@ -305,8 +305,16 @@ export class DockviewComponent
readonly onWillDragGroup: Event<GroupDragEvent> =
this._onWillDragGroup.event;
private readonly _onDidDrop = new Emitter<DockviewDropEvent>();
readonly onDidDrop: Event<DockviewDropEvent> = this._onDidDrop.event;
private readonly _onDidDrop = new Emitter<DockviewDidDropEvent>();
readonly onDidDrop: Event<DockviewDidDropEvent> = this._onDidDrop.event;
private readonly _onWillDrop = new Emitter<DockviewWillDropEvent>();
readonly onWillDrop: Event<DockviewWillDropEvent> = this._onWillDrop.event;
private readonly _onWillShowOverlay =
new Emitter<WillShowOverlayLocationEvent>();
readonly onWillShowOverlay: Event<WillShowOverlayLocationEvent> =
this._onWillShowOverlay.event;
private readonly _onDidRemovePanel = new Emitter<IDockviewPanel>();
readonly onDidRemovePanel: Event<IDockviewPanel> =
@ -380,11 +388,13 @@ export class DockviewComponent
this.overlayRenderContainer,
this._onWillDragPanel,
this._onWillDragGroup,
this._onWillShowOverlay,
this._onDidActivePanelChange,
this._onDidAddPanel,
this._onDidRemovePanel,
this._onDidLayoutFromJSON,
this._onDidDrop,
this._onWillDrop,
Event.any(
this.onDidAddGroup,
this.onDidRemoveGroup
@ -477,6 +487,22 @@ export class DockviewComponent
this.addDisposables(
this._rootDropTarget.onDrop((event) => {
const willDropEvent = new DockviewWillDropEvent({
nativeEvent: event.nativeEvent,
position: event.position,
panel: undefined,
api: this._api,
group: undefined,
getData: getPanelData,
kind: 'content',
});
this._onWillDrop.fire(willDropEvent);
if (willDropEvent.defaultPrevented) {
return;
}
const data = getPanelData();
if (data) {
@ -487,12 +513,16 @@ export class DockviewComponent
'center'
);
} else {
this._onDidDrop.fire({
...event,
api: this._api,
group: null,
getData: getPanelData,
});
this._onDidDrop.fire(
new DockviewDidDropEvent({
nativeEvent: event.nativeEvent,
position: event.position,
panel: undefined,
api: this._api,
group: undefined,
getData: getPanelData,
})
);
}
}),
this._rootDropTarget
@ -1652,11 +1682,18 @@ export class DockviewComponent
this.moveGroupOrPanel(view, groupId, itemId, target, index);
}),
view.model.onDidDrop((event) => {
this._onDidDrop.fire({
...event,
api: this._api,
group: view,
});
this._onDidDrop.fire(event);
}),
view.model.onWillDrop((event) => {
this._onWillDrop.fire(event);
}),
view.model.onWillShowOverlay((event) => {
if (this.options.disableDnd) {
event.event.preventDefault();
return;
}
this._onWillShowOverlay.fire(event);
}),
view.model.onDidAddPanel((event) => {
this._onDidAddPanel.fire(event.panel);

View File

@ -1,9 +1,14 @@
import { DockviewApi } from '../api/component.api';
import { getPanelData, PanelTransfer } from '../dnd/dataTransfer';
import { Position } from '../dnd/droptarget';
import { Position, WillShowOverlayEvent } from '../dnd/droptarget';
import { DockviewComponent } from './dockviewComponent';
import { isAncestor, toggleClass } from '../dom';
import { addDisposableListener, Emitter, Event } from '../events';
import {
addDisposableListener,
DockviewEvent,
Emitter,
Event,
} from '../events';
import { IViewSize } from '../gridview/gridview';
import { CompositeDisposable } from '../lifecycle';
import { IPanel, PanelInitParameters, PanelUpdateEvent } from '../panel/types';
@ -46,15 +51,69 @@ export interface GroupPanelViewState extends CoreGroupOptions {
id: string;
}
export interface GroupviewChangeEvent {
export interface DockviewGroupChangeEvent {
readonly panel: IDockviewPanel;
}
export interface GroupviewDropEvent {
readonly nativeEvent: DragEvent;
readonly position: Position;
readonly index?: number;
getData(): PanelTransfer | undefined;
export class DockviewDidDropEvent extends DockviewEvent {
get nativeEvent(): DragEvent {
return this.options.nativeEvent;
}
get position(): Position {
return this.options.position;
}
get panel(): IDockviewPanel | undefined {
return this.options.panel;
}
get group(): DockviewGroupPanel | undefined {
return this.options.group;
}
get api(): DockviewApi {
return this.options.api;
}
constructor(
private readonly options: {
readonly nativeEvent: DragEvent;
readonly position: Position;
readonly panel?: IDockviewPanel;
getData(): PanelTransfer | undefined;
group?: DockviewGroupPanel;
api: DockviewApi;
}
) {
super();
}
getData(): PanelTransfer | undefined {
return this.options.getData();
}
}
export class DockviewWillDropEvent extends DockviewDidDropEvent {
private readonly _kind: DockviewGroupDropLocation;
get kind(): DockviewGroupDropLocation {
return this._kind;
}
constructor(options: {
readonly nativeEvent: DragEvent;
readonly position: Position;
readonly panel?: IDockviewPanel;
getData(): PanelTransfer | undefined;
kind: DockviewGroupDropLocation;
group?: DockviewGroupPanel;
api: DockviewApi;
}) {
super(options);
this._kind = options.kind;
}
}
export interface IHeader {
@ -63,6 +122,8 @@ export interface IHeader {
export type DockviewGroupPanelLocked = boolean | 'no-drop-target';
export type DockviewGroupDropLocation = 'tab' | 'header_space' | 'content';
export interface IDockviewGroupPanelModel extends IPanel {
readonly isActive: boolean;
readonly size: number;
@ -70,10 +131,11 @@ export interface IDockviewGroupPanelModel extends IPanel {
readonly activePanel: IDockviewPanel | undefined;
readonly header: IHeader;
readonly isContentFocused: boolean;
readonly onDidDrop: Event<GroupviewDropEvent>;
readonly onDidAddPanel: Event<GroupviewChangeEvent>;
readonly onDidRemovePanel: Event<GroupviewChangeEvent>;
readonly onDidActivePanelChange: Event<GroupviewChangeEvent>;
readonly onDidDrop: Event<DockviewDidDropEvent>;
readonly onWillDrop: Event<DockviewWillDropEvent>;
readonly onDidAddPanel: Event<DockviewGroupChangeEvent>;
readonly onDidRemovePanel: Event<DockviewGroupChangeEvent>;
readonly onDidActivePanelChange: Event<DockviewGroupChangeEvent>;
readonly onMove: Event<GroupMoveEvent>;
locked: DockviewGroupPanelLocked;
setActive(isActive: boolean): void;
@ -112,13 +174,17 @@ export interface IDockviewGroupPanelModel extends IPanel {
export type DockviewGroupLocation = 'grid' | 'floating' | 'popout';
export interface WillShowOverlayLocationEvent {
event: WillShowOverlayEvent;
kind: DockviewGroupDropLocation;
}
export class DockviewGroupPanelModel
extends CompositeDisposable
implements IDockviewGroupPanelModel
{
private readonly tabsContainer: ITabsContainer;
private readonly contentContainer: IContentContainer;
// private readonly dropTarget: Droptarget;
private _activePanel: IDockviewPanel | undefined;
private watermark?: IWatermarkRenderer;
private _isGroupActive = false;
@ -143,8 +209,16 @@ export class DockviewGroupPanelModel
private readonly _onMove = new Emitter<GroupMoveEvent>();
readonly onMove: Event<GroupMoveEvent> = this._onMove.event;
private readonly _onDidDrop = new Emitter<GroupviewDropEvent>();
readonly onDidDrop: Event<GroupviewDropEvent> = this._onDidDrop.event;
private readonly _onDidDrop = new Emitter<DockviewDidDropEvent>();
readonly onDidDrop: Event<DockviewDidDropEvent> = this._onDidDrop.event;
private readonly _onWillDrop = new Emitter<DockviewWillDropEvent>();
readonly onWillDrop: Event<DockviewWillDropEvent> = this._onWillDrop.event;
private readonly _onWillShowOverlay =
new Emitter<WillShowOverlayLocationEvent>();
readonly onWillShowOverlay: Event<WillShowOverlayLocationEvent> =
this._onWillShowOverlay.event;
private readonly _onTabDragStart = new Emitter<TabDragEvent>();
readonly onTabDragStart: Event<TabDragEvent> = this._onTabDragStart.event;
@ -153,19 +227,22 @@ export class DockviewGroupPanelModel
readonly onGroupDragStart: Event<GroupDragEvent> =
this._onGroupDragStart.event;
private readonly _onDidAddPanel = new Emitter<GroupviewChangeEvent>();
readonly onDidAddPanel: Event<GroupviewChangeEvent> =
private readonly _onDidAddPanel = new Emitter<DockviewGroupChangeEvent>();
readonly onDidAddPanel: Event<DockviewGroupChangeEvent> =
this._onDidAddPanel.event;
private readonly _onDidRemovePanel = new Emitter<GroupviewChangeEvent>();
readonly onDidRemovePanel: Event<GroupviewChangeEvent> =
private readonly _onDidRemovePanel =
new Emitter<DockviewGroupChangeEvent>();
readonly onDidRemovePanel: Event<DockviewGroupChangeEvent> =
this._onDidRemovePanel.event;
private readonly _onDidActivePanelChange =
new Emitter<GroupviewChangeEvent>();
readonly onDidActivePanelChange: Event<GroupviewChangeEvent> =
new Emitter<DockviewGroupChangeEvent>();
readonly onDidActivePanelChange: Event<DockviewGroupChangeEvent> =
this._onDidActivePanelChange.event;
private readonly _api: DockviewApi;
get element(): HTMLElement {
throw new Error('not supported');
}
@ -279,6 +356,8 @@ export class DockviewGroupPanelModel
toggleClass(this.container, 'groupview', true);
this._api = new DockviewApi(this.accessor);
this.tabsContainer = new TabsContainer(this.accessor, this.groupPanel);
this.contentContainer = new ContentContainer(this.accessor, this);
@ -294,6 +373,7 @@ export class DockviewGroupPanelModel
this.addDisposables(
this._onTabDragStart,
this._onGroupDragStart,
this._onWillShowOverlay,
this.tabsContainer.onTabDragStart((event) => {
this._onTabDragStart.fire(event);
}),
@ -308,6 +388,7 @@ export class DockviewGroupPanelModel
event.index
);
}),
this.contentContainer.onDidFocus(() => {
this.accessor.doSetGroupActive(this.groupPanel, true);
}),
@ -321,9 +402,16 @@ export class DockviewGroupPanelModel
event.position
);
}),
this.tabsContainer.onWillShowOverlay((event) => {
this._onWillShowOverlay.fire(event);
}),
this.contentContainer.dropTarget.onWillShowOverlay((event) => {
this._onWillShowOverlay.fire({ event, kind: 'content' });
}),
this._onMove,
this._onDidChange,
this._onDidDrop,
this._onWillDrop,
this._onDidAddPanel,
this._onDidRemovePanel,
this._onDidActivePanelChange
@ -331,13 +419,13 @@ export class DockviewGroupPanelModel
}
initialize(): void {
if (this.options?.panels) {
if (this.options.panels) {
this.options.panels.forEach((panel) => {
this.doAddPanel(panel);
});
}
if (this.options?.activePanel) {
if (this.options.activePanel) {
this.openPanel(this.options.activePanel);
}
@ -353,7 +441,7 @@ export class DockviewGroupPanelModel
);
this.addDisposables(this._rightHeaderActions);
this._rightHeaderActions.init({
containerApi: new DockviewApi(this.accessor),
containerApi: this._api,
api: this.groupPanel.api,
});
this.tabsContainer.setRightActionsElement(
@ -368,7 +456,7 @@ export class DockviewGroupPanelModel
);
this.addDisposables(this._leftHeaderActions);
this._leftHeaderActions.init({
containerApi: new DockviewApi(this.accessor),
containerApi: this._api,
api: this.groupPanel.api,
});
this.tabsContainer.setLeftActionsElement(
@ -383,7 +471,7 @@ export class DockviewGroupPanelModel
);
this.addDisposables(this._prefixHeaderActions);
this._prefixHeaderActions.init({
containerApi: new DockviewApi(this.accessor),
containerApi: this._api,
api: this.groupPanel.api,
});
this.tabsContainer.setPrefixActionsElement(
@ -721,7 +809,7 @@ export class DockviewGroupPanelModel
if (this.isEmpty && !this.watermark) {
const watermark = this.accessor.createWatermarkComponent();
watermark.init({
containerApi: new DockviewApi(this.accessor),
containerApi: this._api,
group: this.groupPanel,
});
this.watermark = watermark;
@ -773,7 +861,7 @@ export class DockviewGroupPanelModel
return;
}
function getKind(): 'tab' | 'header_space' | 'content' {
function getKind(): DockviewGroupDropLocation {
switch (type) {
case 'header':
return typeof index === 'number' ? 'tab' : 'header_space';
@ -782,7 +870,24 @@ export class DockviewGroupPanelModel
}
}
const kind = getKind();
const panel =
typeof index === 'number' ? this.panels[index] : undefined;
const willDropEvent = new DockviewWillDropEvent({
nativeEvent: event,
position,
panel,
getData: () => getPanelData(),
kind: getKind(),
group: this.groupPanel,
api: this._api,
});
this._onWillDrop.fire(willDropEvent);
if (willDropEvent.defaultPrevented) {
return;
}
const data = getPanelData();
@ -791,7 +896,6 @@ export class DockviewGroupPanelModel
// this is a group move dnd event
const { groupId } = data;
// TODO: intercept
this._onMove.fire({
target: position,
groupId: groupId,
@ -816,7 +920,6 @@ export class DockviewGroupPanelModel
}
}
// TODO: intercept
this._onMove.fire({
target: position,
groupId: data.groupId,
@ -824,12 +927,16 @@ export class DockviewGroupPanelModel
index,
});
} else {
this._onDidDrop.fire({
nativeEvent: event,
position,
index,
getData: () => getPanelData(),
});
this._onDidDrop.fire(
new DockviewDidDropEvent({
nativeEvent: event,
position,
panel,
getData: () => getPanelData(),
group: this.groupPanel,
api: this._api,
})
);
}
}

View File

@ -101,6 +101,7 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions {
defaultRenderer?: DockviewPanelRenderer;
debug?: boolean;
rootOverlayModel?: DroptargetOverlayModel;
disableDnd?: boolean;
}
export interface PanelOptions<P extends object = Parameters> {

View File

@ -24,6 +24,18 @@ export namespace Event {
};
}
export class DockviewEvent {
private _defaultPrevented = false;
get defaultPrevented(): boolean {
return this._defaultPrevented;
}
preventDefault(): void {
this._defaultPrevented = true;
}
}
class LeakageMonitor {
readonly events = new Map<Event<any>, Stacktrace>();

View File

@ -1,7 +1,7 @@
import * as React from 'react';
import {
DockviewComponent,
DockviewDropEvent,
DockviewWillDropEvent,
DockviewDndOverlayEvent,
GroupPanelFrameworkComponentFactory,
DockviewPanelApi,
@ -12,6 +12,7 @@ import {
IHeaderActionsRenderer,
DockviewPanelRenderer,
DroptargetOverlayModel,
DockviewDidDropEvent,
} from 'dockview-core';
import { ReactPanelContentPart } from './reactContentPart';
import { ReactPanelHeaderPart } from './reactHeaderPart';
@ -61,7 +62,8 @@ export interface IDockviewReactProps {
components: PanelCollection<IDockviewPanelProps>;
tabComponents?: PanelCollection<IDockviewPanelHeaderProps>;
watermarkComponent?: React.FunctionComponent<IWatermarkPanelProps>;
onDidDrop?: (event: DockviewDropEvent) => void;
onDidDrop?: (event: DockviewDidDropEvent) => void;
onWillDrop?: (event: DockviewWillDropEvent) => void;
showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean;
hideBorders?: boolean;
className?: string;
@ -81,6 +83,7 @@ export interface IDockviewReactProps {
debug?: boolean;
defaultRenderer?: DockviewPanelRenderer;
rootOverlayModel?: DroptargetOverlayModel;
disableDnd?: boolean;
}
const DEFAULT_REACT_TAB = 'props.defaultTabComponent';
@ -183,6 +186,7 @@ export const DockviewReact = React.forwardRef(
defaultRenderer: props.defaultRenderer,
debug: props.debug,
rootOverlayModel: props.rootOverlayModel,
disableDnd: props.disableDnd,
});
const { clientWidth, clientHeight } = domRef.current;
@ -199,6 +203,15 @@ export const DockviewReact = React.forwardRef(
};
}, []);
React.useEffect(() => {
if (!dockviewRef.current) {
return;
}
dockviewRef.current.updateOptions({
disableDnd: props.disableDnd,
});
}, [props.disableDnd]);
React.useEffect(() => {
if (!dockviewRef.current) {
return () => {
@ -217,6 +230,24 @@ export const DockviewReact = React.forwardRef(
};
}, [props.onDidDrop]);
React.useEffect(() => {
if (!dockviewRef.current) {
return () => {
// noop
};
}
const disposable = dockviewRef.current.onWillDrop((event) => {
if (props.onWillDrop) {
props.onWillDrop(event);
}
});
return () => {
disposable.dispose();
};
}, [props.onWillDrop]);
React.useEffect(() => {
if (!dockviewRef.current) {
return;