pull/5370/head
bunsenstraat 11 months ago
parent 343d909bd3
commit e03b2edc13
  1. 2
      apps/remix-ide/src/app/files/fileProvider.ts
  2. 32
      libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
  3. 50
      libs/remix-ui/workspace/src/lib/components/flat-tree-drop.tsx
  4. 103
      libs/remix-ui/workspace/src/lib/components/flat-tree.tsx
  5. 25
      libs/remix-ui/workspace/src/lib/utils/getEventTarget.ts

@ -287,7 +287,7 @@ export default class FileProvider {
// ^ ret does not accept path starting with '/' // ^ 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) if (cb) cb(null, ret)
return ret return ret
} catch (error) { } catch (error) {

@ -369,13 +369,12 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
}, [props]) }, [props])
const handleTreeClick = (e: SyntheticEvent) => { const handleTreeClick = (event: SyntheticEvent) => {
console.log('tree click', e.target) event.stopPropagation()
// find any parent of e.target that has a data-path attribute console.log('tree click', event.target)
// if found, call handleClickFolder with that path
// if not found, do nothing
let target = e.target as HTMLElement
let target = event.target as HTMLElement
while (target && target.getAttribute && !target.getAttribute('data-path')) { while (target && target.getAttribute && !target.getAttribute('data-path')) {
target = target.parentElement target = target.parentElement
} }
@ -384,11 +383,15 @@ export const FileExplorer = (props: FileExplorerProps) => {
const type = target.getAttribute('data-type') const type = target.getAttribute('data-type')
if (path && type === 'file') { if (path && type === 'file') {
console.log('tree click', path) console.log('tree click', path)
handleClickFile(path, type as WorkspaceElement)
if (state.focusEdit.element !== path) handleClickFile(path, type)
} else if (path && type === 'folder') { } else if (path && type === 'folder') {
console.log('tree click', path) console.log('tree click', path)
handleClickFolder(path, type) if (state.focusEdit.element !== path) handleClickFolder(path, type)
} }
if (props.showIconsMenu === true) props.hideIconsMenu(!props.showIconsMenu)
} }
} }
@ -437,6 +440,8 @@ export const FileExplorer = (props: FileExplorerProps) => {
files={files} files={files}
expandPath={props.expandPath} expandPath={props.expandPath}
handleContextMenu={handleContextMenu} handleContextMenu={handleContextMenu}
moveFile={handleFileMove}
moveFolder={handleFolderMove}
/> />
</div> </div>
</div> </div>
@ -449,14 +454,3 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
export default FileExplorer export default FileExplorer
/*
<RecursiveTree
focusEdit={state.focusEdit}
focusElement={props.focusElement}
focusContext={state.focusContext}
editModeOff={editModeOff}
handleContextMenu={handleContextMenu}
expandPath={props.expandPath}
files={files} />*/

@ -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 (<div
onDrop={onDrop} onDragOver={onDragOver}
>{props.children}</div>)
}

@ -5,6 +5,8 @@ import { ROOT_PATH } from '../utils/constants'
import { getPathIcon } from '@remix-ui/helper'; import { getPathIcon } from '@remix-ui/helper';
import { Virtuoso } from 'react-virtuoso' import { Virtuoso } from 'react-virtuoso'
import { RecursiveItemInput } from './file-recursive-item-input'; import { RecursiveItemInput } from './file-recursive-item-input';
import { FlatTreeDrop } from './flat-tree-drop';
import { getEventTarget } from '../utils/getEventTarget';
interface FlatTreeProps { interface FlatTreeProps {
files: { [x: string]: Record<string, FileType> }, files: { [x: string]: Record<string, FileType> },
@ -16,6 +18,8 @@ interface FlatTreeProps {
handleContextMenu: (pageX: number, pageY: number, path: string, content: string, type: string) => void handleContextMenu: (pageX: number, pageY: number, path: string, content: string, type: string) => void
handleTreeClick: (e: SyntheticEvent) => void handleTreeClick: (e: SyntheticEvent) => void
treeRef: React.MutableRefObject<HTMLDivElement> treeRef: React.MutableRefObject<HTMLDivElement>
moveFile: (dest: string, src: string) => void
moveFolder: (dest: string, src: string) => void
} }
let mouseTimer: any = { let mouseTimer: any = {
@ -24,7 +28,7 @@ let mouseTimer: any = {
} }
export const FlatTree = (props: FlatTreeProps) => { 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 [flatTree, setFlatTree] = useState<{ [x: string]: FileType }>({})
const [hover, setHover] = useState<string>('') const [hover, setHover] = useState<string>('')
const [mouseOverTarget, setMouseOverTarget] = useState<{ const [mouseOverTarget, setMouseOverTarget] = useState<{
@ -37,13 +41,12 @@ export const FlatTree = (props: FlatTreeProps) => {
} }
}>(null) }>(null)
const [showMouseOverTarget, setShowMouseOverTarget] = useState<boolean>(false) const [showMouseOverTarget, setShowMouseOverTarget] = useState<boolean>(false)
const [dragSource, setDragSource] = useState<FileType>()
const [isDragging, setIsDragging] = useState<boolean>(false)
const ref = useRef(null) const ref = useRef(null)
const [size, setSize] = useState(0) const [size, setSize] = useState(0)
const labelClass = (file: FileType) => const labelClass = (file: FileType) =>
props.focusEdit.element === file.path props.focusEdit.element === file.path
? 'bg-light' ? 'bg-light'
@ -72,9 +75,6 @@ export const FlatTree = (props: FlatTreeProps) => {
setFlatTree(flatTree) setFlatTree(flatTree)
}, [props]) }, [props])
const getIndentLevelDiv = (path: string) => { const getIndentLevelDiv = (path: string) => {
// remove double slash // remove double slash
path = path.replace(/\/\//g, '/') path = path.replace(/\/\//g, '/')
@ -83,33 +83,7 @@ export const FlatTree = (props: FlatTreeProps) => {
const pathArray = path.split('/') const pathArray = path.split('/')
const level = pathArray.length - 1 const level = pathArray.length - 1
const indent = level * 10 const indent = level * 10
return (<div style={{ width: `${indent}px` }}></div>) return (<div style={{ paddingLeft: `${indent}px` }}></div>)
}
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
}
}
} }
const handleContextMenu = async (e: any) => { const handleContextMenu = async (e: any) => {
@ -121,22 +95,32 @@ export const FlatTree = (props: FlatTreeProps) => {
} }
} }
const onDragEnd = (event: SyntheticEvent) => { const onDragStart = async (event: SyntheticEvent) => {
console.log('drag end', event) 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) => { useEffect(() => {
event.preventDefault() console.log('drag source', dragSource)
console.log('drop', event) if(isDragging) {
const target = await getEventTarget(event) mouseTimer = {
console.log('drop', target) path: null,
timer: null
}
}
},[isDragging])
const onDragEnd = (event: SyntheticEvent) => {
setIsDragging(false)
console.log('drag end', event)
} }
const onDragOver = async (e: SyntheticEvent) => { const getFlatTreeItem = (path: string) => {
e.preventDefault() console.log('get flat tree item', path, flatTree[path])
//console.log('drag over', e) return flatTree[path]
const target = await getEventTarget(e)
//console.log('drag over', target)
} }
const onMouseMove = async (e: any) => { const onMouseMove = async (e: any) => {
@ -196,14 +180,20 @@ export const FlatTree = (props: FlatTreeProps) => {
<div className={`pr-2 pl-2 ${file.isDirectory ? expandPath && expandPath.includes(file.path) ? 'fa fa-folder-open' : 'fa fa-folder' : getPathIcon(file.path)} caret caret_tv`}></div> <div className={`pr-2 pl-2 ${file.isDirectory ? expandPath && expandPath.includes(file.path) ? 'fa fa-folder-open' : 'fa fa-folder' : getPathIcon(file.path)} caret caret_tv`}></div>
{focusEdit && file.path && focusEdit.element === file.path ? {focusEdit && file.path && focusEdit.element === file.path ?
<RecursiveItemInput editModeOff={editModeOff} file={file}/>: <RecursiveItemInput editModeOff={editModeOff} file={file}/>:
<div draggable={true} className="ml-1 pl-2" data-label-type={file.isDirectory ? 'folder' : 'file'} data-label-path={`${file.path}`} key={index}>{file.name} <div draggable={true} onDragStart={onDragStart} onDragEnd={onDragEnd} className={`ml-1 pl-2 text-nowrap remixui_leaf`} data-label-type={file.isDirectory ? 'folder' : 'file'} data-label-path={`${file.path}`} key={index}>{file.name}
</div>} </div>}
</div>) </div>)
} }
return (<> return (<>
<div onClick={handleTreeClick} onMouseLeave={onMouseLeave} onMouseMove={onMouseMove} onDrop={onDrop} onDragOver={onDragOver} onContextMenu={handleContextMenu}> <FlatTreeDrop
{showMouseOverTarget && mouseOverTarget && dragSource={dragSource}
getFlatTreeItem={getFlatTreeItem}
moveFile={moveFile}
moveFolder={moveFolder}
>
<div onClick={handleTreeClick} onMouseLeave={onMouseLeave} onMouseMove={onMouseMove} onContextMenu={handleContextMenu}>
{showMouseOverTarget && mouseOverTarget && !isDragging &&
<Popover id='popover-basic' <Popover id='popover-basic'
placement='top' placement='top'
ref={ref} ref={ref}
@ -221,13 +211,14 @@ export const FlatTree = (props: FlatTreeProps) => {
{mouseOverTarget && mouseOverTarget.path} {mouseOverTarget && mouseOverTarget.path}
</Popover.Content> </Popover.Content>
</Popover> </Popover>
} }
<Virtuoso <Virtuoso
style={{ height: `${size}px` }} style={{ height: `${size}px` }}
totalCount={Object.keys(flatTree).length} totalCount={Object.keys(flatTree).length}
itemContent={index => Row(index)} itemContent={index => Row(index)}
/> />
</div> </div>
</FlatTreeDrop>
</>) </>)
} }

@ -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
}
}
}
Loading…
Cancel
Save