diff --git a/src/components/tree/__tests__/tree.test.tsx b/src/components/tree/__tests__/tree.test.tsx
index ba0dcd25e..dd928125c 100644
--- a/src/components/tree/__tests__/tree.test.tsx
+++ b/src/components/tree/__tests__/tree.test.tsx
@@ -23,13 +23,6 @@ const mockData: ITreeNodeItemProps[] = [
},
];
-async function dragExpect(fn: jest.Mock, result: any) {
- await waitFor(() => {
- expect(fn).toBeCalled();
- expect(fn.mock.calls[0][0]).toEqual(result);
- });
-}
-
describe('Test the Tree component', () => {
afterEach(cleanup);
@@ -198,11 +191,11 @@ describe('Test the Tree component', () => {
expect(await findByTitle('test2')).toBeInTheDocument();
});
- test('Should support to sort via drag', async () => {
+ test('Should NOT support to sort via drag', async () => {
const data = [
- { id: '1', name: 'test1' },
- { id: '2', name: 'test2' },
- { id: '3', name: 'test3' },
+ { id: '1', name: 'test1', isLeaf: true },
+ { id: '2', name: 'test2', isLeaf: true },
+ { id: '3', name: 'test3', isLeaf: true },
];
const mockFn = jest.fn();
const { findByTitle } = render(
@@ -214,24 +207,47 @@ describe('Test the Tree component', () => {
await findByTitle('test1')
);
- await dragExpect(mockFn, [
- { id: '1', name: 'test1' },
- { id: '3', name: 'test3' },
- { id: '2', name: 'test2' },
- ]);
+ expect(mockFn).not.toBeCalled();
});
- test('Should support to drag into children', async () => {
+ test('Should NOT darg to the its parent node', async () => {
const data = [
{
id: '1',
name: 'test1',
- children: [{ id: '1-1', name: 'test1-1' }],
+ isLeaf: false,
+ children: [{ id: '1-1', name: 'test1-1', isLeaf: true }],
},
+ ];
+ const mockFn = jest.fn();
+ const { findByTitle } = render(
+
+ );
+
+ dragToTargetNode(
+ await findByTitle('test1-1'),
+ await findByTitle('test1')
+ );
+
+ expect(mockFn).not.toBeCalled();
+ });
+
+ test('Should support to drag into children', async () => {
+ const source = { id: '2', name: 'test2', isLeaf: true };
+ const target = { id: '1-1', name: 'test1-1', isLeaf: false };
+ const data = [
{
- id: '2',
- name: 'test2',
+ id: '1',
+ name: 'test1',
+ isLeaf: false,
+ children: [target],
},
+ source,
];
const mockFn = jest.fn();
const { findByTitle } = render(
@@ -244,19 +260,54 @@ describe('Test the Tree component', () => {
);
dragToTargetNode(
- await findByTitle('test2'),
- await findByTitle('test1-1')
+ await findByTitle(source.name),
+ await findByTitle(target.name)
);
- await dragExpect(mockFn, [
+ expect(mockFn).toBeCalled();
+ expect(mockFn.mock.calls[0][0]).toEqual(source);
+ expect(mockFn.mock.calls[0][1]).toEqual(target);
+ });
+
+ test('Should support to drag to the folder rather than a file', async () => {
+ const source = { id: '2-1', name: 'test2-1', isLeaf: true };
+ const target = { id: '1-1', name: 'test1-1', isLeaf: true };
+ const data = [
{
id: '1',
name: 'test1',
- children: [
- { id: '1-1', name: 'test1-1' },
- { id: '2', name: 'test2' },
- ],
+ isLeaf: false,
+ children: [target],
},
- ]);
+ {
+ id: '2',
+ name: 'test2',
+ isLeaf: false,
+ children: [source],
+ },
+ ];
+ const mockFn = jest.fn();
+ const { findByTitle } = render(
+
+ );
+
+ dragToTargetNode(
+ await findByTitle(source.name),
+ await findByTitle(target.name)
+ );
+
+ expect(mockFn).toBeCalled();
+ expect(mockFn.mock.calls[0][0]).toEqual(source);
+ expect(mockFn.mock.calls[0][1]).toEqual({
+ id: '1',
+ name: 'test1',
+ isLeaf: false,
+ children: [target],
+ });
});
});
diff --git a/src/components/tree/index.tsx b/src/components/tree/index.tsx
index cc27a3f45..43b3dcdf1 100644
--- a/src/components/tree/index.tsx
+++ b/src/components/tree/index.tsx
@@ -5,6 +5,7 @@ import { prefixClaName, classNames } from 'mo/common/className';
import type { DataNode } from 'rc-tree/lib/interface';
import { FileTypes } from 'mo/model';
import type { LoadEventData } from 'mo/controller';
+import { TreeViewUtil } from 'mo/services/helper';
export interface ITreeNodeItemProps {
disabled?: boolean;
@@ -26,7 +27,7 @@ export interface ITreeProps extends Partial {
index: number,
isLeaf: boolean
) => JSX.Element | string;
- onDropTree?(treeNode: ITreeNodeItemProps[]): void;
+ onDropTree?(source: ITreeNodeItemProps, target: ITreeNodeItemProps): void;
onLoadData?: (treeNode: LoadEventData) => Promise;
}
@@ -46,66 +47,35 @@ const TreeView = ({
const onDrop = (info) => {
if (!draggable) return;
- const dropId = info.node.data.id;
- const dragId = info.dragNode.data.id;
- const dropPos = info.node.pos.split('-');
- const dropPosition =
- info.dropPosition - Number(dropPos[dropPos.length - 1]);
-
- const loopTree = (
- data: ITreeNodeItemProps[],
- key: string,
- callback: (
- item: ITreeNodeItemProps,
- index: number,
- arr: ITreeNodeItemProps[]
- ) => void
- ) => {
- data.forEach((item, index, arr) => {
- if (item.id === key) {
- return callback(item, index, arr);
- }
- if (item.children) {
- return loopTree(item.children, key, callback);
- }
- });
- };
- const treeData = [...data];
-
- let dragObj;
- loopTree(treeData, dragId, (item, index, arr) => {
- arr.splice(index, 1);
- dragObj = item;
+ const source = info.dragNode;
+ const target = info.node;
+ const treeViewUtil = new TreeViewUtil({
+ id: Number.MAX_SAFE_INTEGER,
+ children: data,
});
-
- if (!info.dropToGap) {
- loopTree(treeData, dropId, (item) => {
- item.children = item.children || [];
- item.children.push(dragObj);
- });
- } else if (
- (info.node.data.children || []).length > 0 &&
- info.node.expanded &&
- dropPosition === 1
- ) {
- loopTree(treeData, dropId, (item) => {
- item.children = item.children || [];
- item.children.unshift(dragObj);
- });
+ if (target.data.isLeaf) {
+ // Can't drag into a file, so the target would to be the parent of this target
+ const obj = treeViewUtil.indexes[target.data.id];
+ const targetParentId = obj.parent!;
+
+ const sourceParentId = treeViewUtil.indexes[source.data.id].parent;
+ // Can't drag under same folder
+ if (targetParentId === sourceParentId) {
+ return;
+ }
+ onDropTree?.(
+ source.data,
+ treeViewUtil.indexes[targetParentId].node!
+ );
} else {
- let ar;
- let i;
- loopTree(treeData, dropId, (item, index, arr) => {
- ar = arr;
- i = index;
- });
- if (dropPosition === -1) {
- ar.splice(i, 0, dragObj);
- } else {
- ar.splice(i + 1, 0, dragObj);
+ const sourceParentId = treeViewUtil.indexes[source.data.id].parent;
+ // Can't drag to the parent node
+ if (sourceParentId === target.data.id) {
+ return;
}
+
+ onDropTree?.(source.data, target.data);
}
- onDropTree?.(treeData);
};
const renderTreeNodes = (
diff --git a/src/controller/explorer/folderTree.tsx b/src/controller/explorer/folderTree.tsx
index 8f3e55b2a..db469047c 100644
--- a/src/controller/explorer/folderTree.tsx
+++ b/src/controller/explorer/folderTree.tsx
@@ -30,7 +30,10 @@ export interface IFolderTreeController {
) => void;
readonly onUpdateFileName?: (file: ITreeNodeItemProps) => void;
readonly onSelectFile?: (file: ITreeNodeItemProps) => void;
- readonly onDropTree?: (treeNode: ITreeNodeItemProps[]) => void;
+ readonly onDropTree?: (
+ source: ITreeNodeItemProps,
+ target: ITreeNodeItemProps
+ ) => void;
readonly onLoadData?: (treeNode: LoadEventData) => Promise;
readonly onRightClick?: (treeNode: ITreeNodeItemProps) => IMenuItemProps[];
}
@@ -124,8 +127,12 @@ export class FolderTreeController
return menus;
};
- public readonly onDropTree = (treeNode: ITreeNodeItemProps[]) => {
- this.folderTreeService.onDropTree(treeNode);
+ public readonly onDropTree = (
+ source: ITreeNodeItemProps,
+ target: ITreeNodeItemProps
+ ) => {
+ // this.folderTreeService.onDropTree(treeNode);
+ this.emit(FolderTreeEvent.onDrop, source, target);
};
public onUpdateFileName = (file: ITreeNodeItemProps) => {
diff --git a/src/model/workbench/explorer/folderTree.tsx b/src/model/workbench/explorer/folderTree.tsx
index 4087d2719..d4bea1f1c 100644
--- a/src/model/workbench/explorer/folderTree.tsx
+++ b/src/model/workbench/explorer/folderTree.tsx
@@ -22,6 +22,7 @@ export enum FolderTreeEvent {
onContextMenuClick = 'folderTree.onContextMenuClick',
onCreate = 'folderTree.onCreate',
onLoadData = 'folderTree.onLoadData',
+ onDrop = 'folderTree.onDrop',
}
export interface IFolderInputEvent {
diff --git a/src/services/workbench/__tests__/folderTreeService.test.ts b/src/services/workbench/__tests__/folderTreeService.test.ts
index 6484ee7f1..872fec136 100644
--- a/src/services/workbench/__tests__/folderTreeService.test.ts
+++ b/src/services/workbench/__tests__/folderTreeService.test.ts
@@ -155,14 +155,14 @@ describe('Test StatusBarService', () => {
});
});
- test('Should support to right click', () => {
+ test('Should support to create event', () => {
expectFnCalled((fn) => {
folderTreeService.onCreate(fn);
folderTreeService.emit(FolderTreeEvent.onCreate);
});
});
- test('Should support to right click', () => {
+ test('Should support to contextMenu event', () => {
expectFnCalled((fn) => {
folderTreeService.onContextMenu(fn);
folderTreeService.emit(FolderTreeEvent.onContextMenuClick);
@@ -175,4 +175,11 @@ describe('Test StatusBarService', () => {
folderTreeService.emit(FolderTreeEvent.onLoadData);
});
});
+
+ test('Should support to subscribe drop tree event', () => {
+ expectFnCalled((fn) => {
+ folderTreeService.onDropTree(fn);
+ folderTreeService.emit(FolderTreeEvent.onDrop);
+ });
+ });
});
diff --git a/src/services/workbench/explorer/folderTreeService.ts b/src/services/workbench/explorer/folderTreeService.ts
index 4385ba875..e4a31dc38 100644
--- a/src/services/workbench/explorer/folderTreeService.ts
+++ b/src/services/workbench/explorer/folderTreeService.ts
@@ -98,7 +98,12 @@ export interface IFolderTreeService extends Component {
* Listen to drop event
* @param treeData
*/
- onDropTree(treeData: ITreeNodeItemProps[]): void;
+ onDropTree(
+ callback: (
+ source: ITreeNodeItemProps,
+ target: ITreeNodeItemProps
+ ) => void
+ ): void;
/**
* Listen to right click event
* @param callback
@@ -345,12 +350,13 @@ export class FolderTreeService
this.subscribe(FolderTreeEvent.onSelectFile, callback);
}
- public onDropTree = (treeData: ITreeNodeItemProps[]) => {
- this.setState({
- folderTree: Object.assign(this.state.folderTree?.data, {
- data: treeData,
- }),
- });
+ public onDropTree = (
+ callback: (
+ source: ITreeNodeItemProps,
+ target: ITreeNodeItemProps
+ ) => void
+ ) => {
+ this.subscribe(FolderTreeEvent.onDrop, callback);
};
public onRightClick = (
diff --git a/src/workbench/sidebar/__tests__/folderTree.test.tsx b/src/workbench/sidebar/__tests__/folderTree.test.tsx
index 02df35266..0bb1a0331 100644
--- a/src/workbench/sidebar/__tests__/folderTree.test.tsx
+++ b/src/workbench/sidebar/__tests__/folderTree.test.tsx
@@ -200,12 +200,8 @@ describe('The FolderTree Component', () => {
dragToTargetNode(getByTitle('file'), getByTitle('folder'));
expect(mockFn).toBeCalled();
- expect(mockFn.mock.calls[0][0]).toEqual([
- {
- ...mockTreeData[0],
- children: [mockFolder, mockFile],
- },
- ]);
+ expect(mockFn.mock.calls[0][0]).toEqual(mockFile);
+ expect(mockFn.mock.calls[0][1]).toEqual(mockFolder);
});
test('Should suppor to init contextMenu', () => {
diff --git a/src/workbench/sidebar/explore/folderTree.tsx b/src/workbench/sidebar/explore/folderTree.tsx
index 277a0fde0..978431bcd 100644
--- a/src/workbench/sidebar/explore/folderTree.tsx
+++ b/src/workbench/sidebar/explore/folderTree.tsx
@@ -1,6 +1,5 @@
import 'reflect-metadata';
import React, { memo, useRef, useEffect, useLayoutEffect } from 'react';
-import cloneDeep from 'lodash/cloneDeep';
import { IFolderTree, IFolderTreeSubItem } from 'mo/model';
import { select, getEventPosition } from 'mo/common/dom';
import Tree, { ITreeNodeItemProps } from 'mo/components/tree';
@@ -207,10 +206,8 @@ const FolderTree: React.FunctionComponent = (props) => {
);
};
- const handleDropTree = (treeData) => {
- const newFolderTreeData = cloneDeep(data);
- newFolderTreeData[0].children = treeData;
- onDropTree?.(newFolderTreeData);
+ const handleDropTree = (source, target) => {
+ onDropTree?.(source, target);
};
useEffect(() => {
diff --git a/stories/extensions/test/index.tsx b/stories/extensions/test/index.tsx
index 218a8086a..adf3d1e2c 100644
--- a/stories/extensions/test/index.tsx
+++ b/stories/extensions/test/index.tsx
@@ -183,5 +183,9 @@ export const ExtendTestPane: IExtension = {
};
molecule.editor.open(tabData);
});
+
+ molecule.folderTree.onDropTree((source, target) => {
+ console.log(source, target);
+ });
},
};