mirror of
https://github.com/mathuo/dockview
synced 2025-08-28 04:56:22 +00:00
feat: reduce .onDidDrop nesting and demo work
This commit is contained in:
parent
005d802a99
commit
0da044671f
135
packages/dockview-demo/src/services/sidebarItem.tsx
Normal file
135
packages/dockview-demo/src/services/sidebarItem.tsx
Normal file
@ -0,0 +1,135 @@
|
||||
import { ViewContainer } from './viewContainer';
|
||||
import * as React from 'react';
|
||||
import { toggleClass } from '../dom';
|
||||
|
||||
export const Container = (props: {
|
||||
container: ViewContainer;
|
||||
isActive: boolean;
|
||||
onDragOver: (e: React.DragEvent) => void;
|
||||
onDrop: (e: React.DragEvent, direction: 'top' | 'bottom') => void;
|
||||
onClick: (e: React.MouseEvent) => void;
|
||||
}) => {
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
const [selection, setSelection] = React.useState<
|
||||
'top' | 'bottom' | undefined
|
||||
>(undefined);
|
||||
const isDragging = React.useRef<boolean>(false);
|
||||
|
||||
const [dragEntered, setDragEntered] = React.useState<boolean>(false);
|
||||
|
||||
const onDragOver = (e: React.DragEvent) => {
|
||||
if (isDragging.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
setDragEntered(true);
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const target = e.target as HTMLDivElement;
|
||||
|
||||
const width = target.clientWidth;
|
||||
const height = target.clientHeight;
|
||||
|
||||
if (width === 0 || height === 0) {
|
||||
return; // avoid div!0
|
||||
}
|
||||
|
||||
const x = e.nativeEvent.offsetX;
|
||||
const y = e.nativeEvent.offsetY;
|
||||
const xp = (100 * x) / width;
|
||||
const yp = (100 * y) / height;
|
||||
|
||||
const isTop = yp < 50;
|
||||
const isBottom = yp >= 50;
|
||||
|
||||
setSelection(isTop ? 'top' : 'bottom');
|
||||
|
||||
props.onDragOver(e);
|
||||
};
|
||||
|
||||
const onDragLeave = (e: React.DragEvent) => {
|
||||
if (isDragging.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
setDragEntered(false);
|
||||
|
||||
setSelection(undefined);
|
||||
};
|
||||
|
||||
const onDrop = (e: React.DragEvent) => {
|
||||
if (isDragging.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
setDragEntered(false);
|
||||
|
||||
props.onDrop(e, selection);
|
||||
|
||||
setSelection(undefined);
|
||||
};
|
||||
|
||||
const onDragEnter = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const onDragStart = (e: React.DragEvent) => {
|
||||
isDragging.current = true;
|
||||
|
||||
e.dataTransfer.setData(
|
||||
'application/json',
|
||||
JSON.stringify({ container: props.container.id })
|
||||
);
|
||||
};
|
||||
|
||||
const onDragEnd = (e: React.DragEvent) => {
|
||||
isDragging.current = false;
|
||||
|
||||
setDragEntered(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
draggable={true}
|
||||
onClick={props.onClick}
|
||||
onDragOver={onDragOver}
|
||||
onDragEnter={onDragEnter}
|
||||
onDragStart={onDragStart}
|
||||
onDragLeave={onDragLeave}
|
||||
onDragEnd={onDragEnd}
|
||||
onDrop={onDrop}
|
||||
style={{
|
||||
borderLeft: props.isActive
|
||||
? '1px solid white'
|
||||
: '1px solid transparent',
|
||||
}}
|
||||
className="container-item"
|
||||
>
|
||||
{dragEntered && (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '0px',
|
||||
left: '0px',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
backgroundColor: 'transparent',
|
||||
boxSizing: 'border-box',
|
||||
borderTop: selection === 'top' ? '2px solid white' : '',
|
||||
borderBottom:
|
||||
selection === 'bottom' ? '2px solid white' : '',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
style={{ fontSize: '30px' }}
|
||||
className="material-icons-outlined"
|
||||
>
|
||||
{props.container.icon}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -45,7 +45,7 @@ export class PaneviewContainer implements ViewContainer<SerializedPaneview> {
|
||||
}
|
||||
|
||||
get views() {
|
||||
return this._views;
|
||||
return [...this._views];
|
||||
}
|
||||
|
||||
get schema(): SerializedPaneview | undefined {
|
||||
|
@ -34,6 +34,7 @@ export interface IViewService extends IDisposable {
|
||||
targetLocation: number
|
||||
): void;
|
||||
insertContainerAfter(source: ViewContainer, target: ViewContainer): void;
|
||||
insertContainerBefore(source: ViewContainer, target: ViewContainer): void;
|
||||
addViews(view: View, viewContainer: ViewContainer, location?: number): void;
|
||||
removeViews(removeViews: View[], viewContainer: ViewContainer): void;
|
||||
getViewContainer(id: string): ViewContainer | undefined;
|
||||
@ -103,6 +104,23 @@ export class ViewService implements IViewService {
|
||||
this._onDidContainersChange.fire();
|
||||
}
|
||||
|
||||
insertContainerBefore(source: ViewContainer, target: ViewContainer): void {
|
||||
const sourceIndex = this._viewContainers.findIndex(
|
||||
(c) => c.id === source.id
|
||||
);
|
||||
|
||||
const view = this._viewContainers.splice(sourceIndex, 1)[0];
|
||||
|
||||
const targetIndex = this._viewContainers.findIndex(
|
||||
(c) => c.id === target.id
|
||||
);
|
||||
|
||||
this._viewContainers.splice(Math.max(targetIndex, 0), 0, view);
|
||||
this._viewContainers = [...this._viewContainers];
|
||||
|
||||
this._onDidContainersChange.fire();
|
||||
}
|
||||
|
||||
addContainer(container: ViewContainer): void {
|
||||
this._viewContainers = [...this._viewContainers, container];
|
||||
this._activeViewContainerId = container.id;
|
||||
|
@ -3,3 +3,12 @@
|
||||
background-color: green !important;
|
||||
}
|
||||
}
|
||||
|
||||
.container-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 48px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import { IViewService, ViewService } from './viewService';
|
||||
import { DefaultView } from './view';
|
||||
import { RegisteredView, VIEW_REGISTRY } from './viewRegistry';
|
||||
import { toggleClass } from '../dom';
|
||||
import { Container } from './sidebarItem';
|
||||
import './widgets.scss';
|
||||
|
||||
class ViewServiceModel {
|
||||
@ -172,7 +173,8 @@ export const Activitybar = (props: IGridviewPanelProps) => {
|
||||
};
|
||||
|
||||
const onContainerDrop = (targetContainer: ViewContainer) => (
|
||||
event: React.DragEvent
|
||||
event: React.DragEvent,
|
||||
direction: 'top' | 'bottom'
|
||||
) => {
|
||||
const data = event.dataTransfer.getData('application/json');
|
||||
if (data) {
|
||||
@ -180,10 +182,21 @@ export const Activitybar = (props: IGridviewPanelProps) => {
|
||||
const sourceContainer = viewService.model.getViewContainer(
|
||||
container
|
||||
);
|
||||
viewService.model.insertContainerAfter(
|
||||
sourceContainer,
|
||||
targetContainer
|
||||
);
|
||||
|
||||
switch (direction) {
|
||||
case 'bottom':
|
||||
viewService.model.insertContainerAfter(
|
||||
sourceContainer,
|
||||
targetContainer
|
||||
);
|
||||
break;
|
||||
case 'top':
|
||||
viewService.model.insertContainerBefore(
|
||||
sourceContainer,
|
||||
targetContainer
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -204,48 +217,31 @@ export const Activitybar = (props: IGridviewPanelProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const onDragOver = (container: ViewContainer) => (e: React.DragEvent) => {
|
||||
const api = registry.get<GridviewApi>('gridview');
|
||||
|
||||
const sidebarPanel = api.getPanel('sidebar');
|
||||
if (!sidebarPanel.api.isVisible) {
|
||||
api.setVisible(sidebarPanel, true);
|
||||
sidebarPanel.focus();
|
||||
}
|
||||
|
||||
viewService.model.setActiveViewContainer(container.id);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ background: 'rgb(51,51,51)', cursor: 'pointer' }}>
|
||||
{containers.map((container, i) => {
|
||||
const isActive = activeContainerid === container.id;
|
||||
return (
|
||||
<div
|
||||
onClick={onClick(container)}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
onClick(container, true)(e);
|
||||
}}
|
||||
onDragEnter={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
draggable={true}
|
||||
onDragStart={(e) => {
|
||||
e.dataTransfer.setData(
|
||||
'application/json',
|
||||
JSON.stringify({ container: container.id })
|
||||
);
|
||||
}}
|
||||
onDrop={onContainerDrop(container)}
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '48px',
|
||||
boxSizing: 'border-box',
|
||||
borderLeft: isActive
|
||||
? '1px solid white'
|
||||
: '1px solid transparent',
|
||||
}}
|
||||
<Container
|
||||
key={i}
|
||||
>
|
||||
{/* {container.id} */}
|
||||
<span
|
||||
style={{ fontSize: '30px' }}
|
||||
className="material-icons-outlined"
|
||||
>
|
||||
{container.icon}
|
||||
</span>
|
||||
</div>
|
||||
container={container}
|
||||
isActive={isActive}
|
||||
onDragOver={onDragOver(container)}
|
||||
onClick={onClick(container)}
|
||||
onDrop={onContainerDrop(container)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<ExtraSpace onNewContainer={onNewContainer} />
|
||||
@ -360,25 +356,40 @@ export const SidebarPart = (props: { id: string }) => {
|
||||
};
|
||||
|
||||
const onDidDrop = (event: PaneviewDropEvent) => {
|
||||
const data = event.event.getData();
|
||||
const data = event.getData();
|
||||
|
||||
const containerData = event.event.dataTransfer.getData(
|
||||
'application/json'
|
||||
);
|
||||
|
||||
if (containerData) {
|
||||
const { container } = JSON.parse(containerData);
|
||||
|
||||
const sourceContainer = viewService.model.getViewContainer(
|
||||
container
|
||||
);
|
||||
const targetContainer = viewService.model.getViewContainer(
|
||||
props.id
|
||||
);
|
||||
|
||||
sourceContainer.views.forEach((v) => {
|
||||
viewService.model.moveViewToLocation(v, targetContainer, 0);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetPanel = event.event.panel;
|
||||
const targetPanel = event.panel;
|
||||
const allPanels = event.api.getPanels();
|
||||
let toIndex = allPanels.indexOf(targetPanel);
|
||||
|
||||
// if (
|
||||
// event.event.position === Position.Left ||
|
||||
// event.event.position === Position.Top
|
||||
// ) {
|
||||
// toIndex = Math.max(0, toIndex - 1);
|
||||
// }
|
||||
if (
|
||||
event.event.position === Position.Right ||
|
||||
event.event.position === Position.Bottom
|
||||
event.position === Position.Right ||
|
||||
event.position === Position.Bottom
|
||||
) {
|
||||
toIndex = Math.min(allPanels.length, toIndex + 1);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
PanePanelInitParameter,
|
||||
PaneviewPanel,
|
||||
} from './paneviewPanel';
|
||||
import { addClasses } from '../dom';
|
||||
|
||||
interface ViewContainer {
|
||||
readonly title: string;
|
||||
@ -56,9 +57,15 @@ export abstract class DraggablePaneviewPanel extends PaneviewPanel {
|
||||
}
|
||||
|
||||
private initDragFeatures() {
|
||||
if (!this.header) {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = this.id;
|
||||
this.header!.draggable = true;
|
||||
this.header!.tabIndex = 0;
|
||||
this.header.draggable = true;
|
||||
this.header.tabIndex = 0;
|
||||
|
||||
addClasses(this.header, 'pane-draggable');
|
||||
|
||||
this.handler = new (class PaneDragHandler extends DragHandler {
|
||||
getData(): IDisposable {
|
||||
@ -75,7 +82,7 @@ export abstract class DraggablePaneviewPanel extends PaneviewPanel {
|
||||
},
|
||||
};
|
||||
}
|
||||
})(this.header!);
|
||||
})(this.header);
|
||||
|
||||
this.target = new Droptarget(this.element, {
|
||||
validOverlays: 'vertical',
|
||||
|
@ -46,6 +46,10 @@
|
||||
position: relative;
|
||||
outline: none;
|
||||
|
||||
&.pane-draggable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
&:before {
|
||||
|
@ -22,9 +22,8 @@ export interface IPaneviewPanelProps<T extends {} = Record<string, any>>
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface PaneviewDropEvent {
|
||||
export interface PaneviewDropEvent extends PaneviewDropEvent2 {
|
||||
api: PaneviewApi;
|
||||
event: PaneviewDropEvent2;
|
||||
}
|
||||
|
||||
export interface IPaneviewReactProps {
|
||||
@ -128,7 +127,7 @@ export const PaneviewReact = React.forwardRef(
|
||||
const disposable = paneview.onDidDrop((event) => {
|
||||
if (props.onDidDrop) {
|
||||
props.onDidDrop({
|
||||
event,
|
||||
...event,
|
||||
api: new PaneviewApi(paneview),
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user