diff --git a/apps/remix-ide/src/app/files/fileProvider.ts b/apps/remix-ide/src/app/files/fileProvider.ts index a0368aaf73..81c8c740ec 100644 --- a/apps/remix-ide/src/app/files/fileProvider.ts +++ b/apps/remix-ide/src/app/files/fileProvider.ts @@ -287,7 +287,7 @@ export default class FileProvider { // ^ ret does not accept path starting with '/' } } - console.log(`resolveDirectory ${path} took ${Date.now() - startTime} ms`) + //console.log(`resolveDirectory ${path} took ${Date.now() - startTime} ms`) if (cb) cb(null, ret) return ret } catch (error) { diff --git a/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx b/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx index 09db5cb396..c6d714691e 100644 --- a/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx +++ b/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx @@ -369,13 +369,12 @@ export const FileExplorer = (props: FileExplorerProps) => { } }, [props]) - const handleTreeClick = (e: SyntheticEvent) => { - console.log('tree click', e.target) - // find any parent of e.target that has a data-path attribute - // if found, call handleClickFolder with that path - // if not found, do nothing + const handleTreeClick = (event: SyntheticEvent) => { + event.stopPropagation() + console.log('tree click', event.target) - let target = e.target as HTMLElement + + let target = event.target as HTMLElement while (target && target.getAttribute && !target.getAttribute('data-path')) { target = target.parentElement } @@ -384,11 +383,15 @@ export const FileExplorer = (props: FileExplorerProps) => { const type = target.getAttribute('data-type') if (path && type === 'file') { console.log('tree click', path) - handleClickFile(path, type as WorkspaceElement) + + if (state.focusEdit.element !== path) handleClickFile(path, type) + } else if (path && type === 'folder') { console.log('tree click', path) - handleClickFolder(path, type) + if (state.focusEdit.element !== path) handleClickFolder(path, type) + } + if (props.showIconsMenu === true) props.hideIconsMenu(!props.showIconsMenu) } } @@ -436,7 +439,9 @@ export const FileExplorer = (props: FileExplorerProps) => { editModeOff={editModeOff} files={files} expandPath={props.expandPath} - handleContextMenu={handleContextMenu} + handleContextMenu={handleContextMenu} + moveFile={handleFileMove} + moveFolder={handleFolderMove} /> @@ -449,14 +454,3 @@ export const FileExplorer = (props: FileExplorerProps) => { } export default FileExplorer - -/* - - */ diff --git a/libs/remix-ui/workspace/src/lib/components/flat-tree-drop.tsx b/libs/remix-ui/workspace/src/lib/components/flat-tree-drop.tsx new file mode 100644 index 0000000000..eb13a760e2 --- /dev/null +++ b/libs/remix-ui/workspace/src/lib/components/flat-tree-drop.tsx @@ -0,0 +1,50 @@ +import React, { SyntheticEvent, startTransition, useEffect, useRef, useState } from 'react' +import { FileType } from '../types' +import { getEventTarget } from '../utils/getEventTarget' +import { extractParentFromKey } from '@remix-ui/helper' +interface FlatTreeDropProps { + moveFile: (dest: string, src: string) => void + moveFolder: (dest: string, src: string) => void + getFlatTreeItem: (path: string) => FileType + dragSource: FileType + children: React.ReactNode +} +export const FlatTreeDrop = (props: FlatTreeDropProps) => { + + const { getFlatTreeItem, dragSource, moveFile, moveFolder } = props + const onDragOver = async (e: SyntheticEvent) => { + //setShowMouseOverTarget(false) + e.preventDefault() + const target = await getEventTarget(e) + } + + const onDrop = async (event: SyntheticEvent) => { + event.preventDefault() + console.log('drop', event) + const target = await getEventTarget(event) + const dragDestination = getFlatTreeItem(target.path) + console.log(getFlatTreeItem) + console.log('drop', target, dragDestination, dragSource) + + if (dragDestination.isDirectory) { + if (dragSource.isDirectory) { + moveFolder(dragDestination.path, dragSource.path) + } else { + moveFile(dragDestination.path, dragSource.path) + } + } else { + const path = extractParentFromKey(dragDestination.path) || '/' + + if (dragSource.isDirectory) { + moveFolder(path, dragSource.path) + } else { + moveFile(path, dragSource.path) + } + } + } + + + return (
{props.children}
) +} \ No newline at end of file diff --git a/libs/remix-ui/workspace/src/lib/components/flat-tree.tsx b/libs/remix-ui/workspace/src/lib/components/flat-tree.tsx index 3bcac80525..2428da3bd2 100644 --- a/libs/remix-ui/workspace/src/lib/components/flat-tree.tsx +++ b/libs/remix-ui/workspace/src/lib/components/flat-tree.tsx @@ -5,6 +5,8 @@ import { ROOT_PATH } from '../utils/constants' import { getPathIcon } from '@remix-ui/helper'; import { Virtuoso } from 'react-virtuoso' import { RecursiveItemInput } from './file-recursive-item-input'; +import { FlatTreeDrop } from './flat-tree-drop'; +import { getEventTarget } from '../utils/getEventTarget'; interface FlatTreeProps { files: { [x: string]: Record }, @@ -16,6 +18,8 @@ interface FlatTreeProps { handleContextMenu: (pageX: number, pageY: number, path: string, content: string, type: string) => void handleTreeClick: (e: SyntheticEvent) => void treeRef: React.MutableRefObject + moveFile: (dest: string, src: string) => void + moveFolder: (dest: string, src: string) => void } let mouseTimer: any = { @@ -24,7 +28,7 @@ let mouseTimer: any = { } export const FlatTree = (props: FlatTreeProps) => { - const { files, expandPath, focusEdit, editModeOff, handleTreeClick } = props + const { files, expandPath, focusEdit, editModeOff, handleTreeClick, moveFile, moveFolder } = props const [flatTree, setFlatTree] = useState<{ [x: string]: FileType }>({}) const [hover, setHover] = useState('') const [mouseOverTarget, setMouseOverTarget] = useState<{ @@ -37,13 +41,12 @@ export const FlatTree = (props: FlatTreeProps) => { } }>(null) const [showMouseOverTarget, setShowMouseOverTarget] = useState(false) + const [dragSource, setDragSource] = useState() + const [isDragging, setIsDragging] = useState(false) const ref = useRef(null) const [size, setSize] = useState(0) - - - const labelClass = (file: FileType) => props.focusEdit.element === file.path ? 'bg-light' @@ -72,9 +75,6 @@ export const FlatTree = (props: FlatTreeProps) => { setFlatTree(flatTree) }, [props]) - - - const getIndentLevelDiv = (path: string) => { // remove double slash path = path.replace(/\/\//g, '/') @@ -83,33 +83,7 @@ export const FlatTree = (props: FlatTreeProps) => { const pathArray = path.split('/') const level = pathArray.length - 1 const indent = level * 10 - return (
) - } - - const getEventTarget = async (e: any, useLabel: boolean = false) => { - let target = e.target as HTMLElement - while (target && target.getAttribute && !target.getAttribute(useLabel? 'data-label-path' : 'data-path')) { - target = target.parentElement - } - if (target && target.getAttribute) { - const path = target.getAttribute(useLabel? 'data-label-path' : 'data-path') - const type = target.getAttribute(useLabel? 'data-label-type' : 'data-type') - const position = target.getBoundingClientRect() - // get size of element - - const endPosition = { - top: position.top - position.height * 2 + 4, - left: position.left , - } - - const content = target.textContent - return { - path, - type, - content, - position: endPosition - } - } + return (
) } const handleContextMenu = async (e: any) => { @@ -121,22 +95,32 @@ export const FlatTree = (props: FlatTreeProps) => { } } - const onDragEnd = (event: SyntheticEvent) => { - console.log('drag end', event) + const onDragStart = async (event: SyntheticEvent) => { + console.log('drag start', event) + const target = await getEventTarget(event) + console.log(flatTree[target.path], target.path) + setDragSource(flatTree[target.path]) + setIsDragging(true) } - const onDrop = async (event: SyntheticEvent) => { - event.preventDefault() - console.log('drop', event) - const target = await getEventTarget(event) - console.log('drop', target) + useEffect(() => { + console.log('drag source', dragSource) + if(isDragging) { + mouseTimer = { + path: null, + timer: null + } + } + },[isDragging]) + + const onDragEnd = (event: SyntheticEvent) => { + setIsDragging(false) + console.log('drag end', event) } - const onDragOver = async (e: SyntheticEvent) => { - e.preventDefault() - //console.log('drag over', e) - const target = await getEventTarget(e) - //console.log('drag over', target) + const getFlatTreeItem = (path: string) => { + console.log('get flat tree item', path, flatTree[path]) + return flatTree[path] } const onMouseMove = async (e: any) => { @@ -196,14 +180,20 @@ export const FlatTree = (props: FlatTreeProps) => {
{focusEdit && file.path && focusEdit.element === file.path ? : -
{file.name} +
{file.name}
}
) } return (<> -
- {showMouseOverTarget && mouseOverTarget && + +
+ {showMouseOverTarget && mouseOverTarget && !isDragging && { {mouseOverTarget && mouseOverTarget.path} - } - Row(index)} - /> -
+ } + Row(index)} + /> +
+ ) } \ No newline at end of file diff --git a/libs/remix-ui/workspace/src/lib/utils/getEventTarget.ts b/libs/remix-ui/workspace/src/lib/utils/getEventTarget.ts new file mode 100644 index 0000000000..e61cc7a226 --- /dev/null +++ b/libs/remix-ui/workspace/src/lib/utils/getEventTarget.ts @@ -0,0 +1,25 @@ +export const getEventTarget = async (e: any, useLabel: boolean = false) => { + let target = e.target as HTMLElement + while (target && target.getAttribute && !target.getAttribute(useLabel? 'data-label-path' : 'data-path')) { + target = target.parentElement + } + if (target && target.getAttribute) { + const path = target.getAttribute(useLabel? 'data-label-path' : 'data-path') + const type = target.getAttribute(useLabel? 'data-label-type' : 'data-type') + const position = target.getBoundingClientRect() + // get size of element + + const endPosition = { + top: position.top - position.height * 2 + 4, + left: position.left , + } + + const content = target.textContent + return { + path, + type, + content, + position: endPosition + } + } +} \ No newline at end of file