feat: reduce .onDidDrop nesting and demo work

This commit is contained in:
mathuo 2021-10-24 17:05:34 +01:00
parent 005d802a99
commit 0da044671f
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
8 changed files with 241 additions and 58 deletions

View 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>
);
};

View File

@ -45,7 +45,7 @@ export class PaneviewContainer implements ViewContainer<SerializedPaneview> {
}
get views() {
return this._views;
return [...this._views];
}
get schema(): SerializedPaneview | undefined {

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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',

View File

@ -46,6 +46,10 @@
position: relative;
outline: none;
&.pane-draggable {
cursor: pointer;
}
&:focus,
&:focus-within {
&:before {

View File

@ -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),
});
}