flattentree
filip mertens 11 months ago
parent 0fbb26093e
commit c8e061b95e
  1. 27
      libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
  2. 5
      libs/remix-ui/workspace/src/lib/components/file-recursive-item-input.tsx
  3. 36
      libs/remix-ui/workspace/src/lib/components/flat-tree.tsx
  4. 2
      libs/remix-ui/workspace/src/lib/contexts/index.ts
  5. 117
      libs/remix-ui/workspace/src/lib/reducers/workspace.ts
  6. 2
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  7. 1
      libs/remix-ui/workspace/src/lib/types/index.ts
  8. 13
      libs/remix-ui/workspace/src/lib/utils/index.ts

@ -8,11 +8,8 @@ import { FileExplorerProps, FileType, WorkSpaceState, WorkspaceElement } from '.
import '../css/file-explorer.css'
import { checkSpecialChars, extractNameFromKey, extractParentFromKey, getPathIcon, joinPath } from '@remix-ui/helper'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Drag, Draggable } from '@remix-ui/drag-n-drop'
import { ROOT_PATH } from '../utils/constants'
import { fileKeySort } from '../utils'
import { moveFileIsAllowed, moveFolderIsAllowed } from '../actions'
import { RecursiveTree } from './file-recursive-tree'
import { FlatTree } from './flat-tree'
export const FileExplorer = (props: FileExplorerProps) => {
@ -22,6 +19,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
contextMenuItems,
removedContextMenuItems,
files,
flatTree,
workspaceState,
toGist,
addMenuItems,
@ -36,8 +34,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
const [state, setState] = useState<WorkSpaceState>(workspaceState)
const [isPending, startTransition] = useTransition();
const treeRef = useRef<HTMLDivElement>(null)
const [childrenKeys, setChildrenKeys] = useState<string[]>([])
useEffect(() => {
if (contextMenuItems) {
@ -349,26 +345,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
console.log('FE RENDER', ROOT_PATH)
}, [])
const dragStatus = (isDragged: boolean) => {
props.dragStatus(isDragged)
}
useEffect(() => {
console.log('fe props', props)
return
console.log('fe props', files[ROOT_PATH])
if (files[ROOT_PATH]) {
try {
const children: FileType[] = files[ROOT_PATH] as any
setChildrenKeys(fileKeySort(children))
} catch (error) {
setChildrenKeys(Object.keys(files[ROOT_PATH]))
}
} else {
setChildrenKeys([])
}
}, [props])
const handleTreeClick = (event: SyntheticEvent) => {
event.stopPropagation()
console.log('tree click', event.target)
@ -438,6 +414,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
focusContext={state.focusContext}
editModeOff={editModeOff}
files={files}
flatTree={flatTree}
fileState={fileState}
expandPath={props.expandPath}
handleContextMenu={handleContextMenu}

@ -45,13 +45,14 @@ export const RecursiveItemInput = (props: RecursiveItemInputProps) => {
},[])
return(<input
onBlur={handleEditBlur}
onBlur={handleEditBlur}
data-id='/blank'
onKeyDown={handleKeyDown}
ref={ref}
style={{
height: '1.5em',
}}
className='form-control p-0 ml-2'
className='form-control p-0 ml-2 remixui_items'
onFocus={handleFocus}
onChange={e => setValue(e.target.value)}
defaultValue={file.name}>

@ -8,9 +8,11 @@ import { RecursiveItemInput } from './file-recursive-item-input';
import { FlatTreeDrop } from './flat-tree-drop';
import { getEventTarget } from '../utils/getEventTarget';
import { fileDecoration } from '@remix-ui/file-decorators';
import { focusElement } from '../actions/payload';
interface FlatTreeProps {
files: { [x: string]: Record<string, FileType> },
flatTree: { [x: string]: FileType },
expandPath: string[],
focusEdit: { element: string; type: string; isNew: boolean; lastEdit: string }
editModeOff: (content: string) => void
@ -30,8 +32,8 @@ let mouseTimer: any = {
}
export const FlatTree = (props: FlatTreeProps) => {
const { files, expandPath, focusEdit, editModeOff, handleTreeClick, moveFile, moveFolder, fileState } = props
const [flatTree, setFlatTree] = useState<{ [x: string]: FileType }>({})
const { files, flatTree, expandPath, focusEdit, editModeOff, handleTreeClick, moveFile, moveFolder, fileState, focusElement } = props
//const [flatTree, setFlatTree] = useState<{ [x: string]: FileType }>({})
const [hover, setHover] = useState<string>('')
const [mouseOverTarget, setMouseOverTarget] = useState<{
path: string,
@ -61,21 +63,11 @@ export const FlatTree = (props: FlatTreeProps) => {
: ''
useEffect(() => {
console.log('flat files changed', files, ROOT_PATH)
const flatTree = {}
const mapChild = (file: FileType) => {
flatTree[file.path] = file
expandPath && expandPath.includes(file.path) &&
file.child && Object.keys(file.child).map((key) => {
mapChild(file.child[key])
})
}
files && files[ROOT_PATH] && Object.keys(files[ROOT_PATH]).map((key) => {
mapChild(files[ROOT_PATH][key])
})
console.log('flat tree', flatTree)
setFlatTree(flatTree)
}, [props])
console.log('PROP flat tree changed', props.flatTree)
},[props.flatTree])
useEffect(() => {
}, [expandPath, focusEdit, focusElement])
const getIndentLevelDiv = (path: string) => {
// remove double slash
@ -84,7 +76,7 @@ export const FlatTree = (props: FlatTreeProps) => {
path = path.replace(/^\//g, '')
const pathArray = path.split('/')
const level = pathArray.length - 1
const indent = level * 10
const indent = level * 4
return (<div style={{ paddingLeft: `${indent}px` }}></div>)
}
@ -181,11 +173,13 @@ export const FlatTree = (props: FlatTreeProps) => {
const node = Object.keys(flatTree)[index]
const file = flatTree[node]
//console.log('node', node)
return (<div
return (<li
className={`d-flex flex-row align-items-center ${labelClass(file)}`}
onMouseOver={() => setHover(file.path)}
onMouseOut={() => setHover(file.path)}
data-type={file.isDirectory ? 'folder' : 'file'} data-path={`${file.path}`} data-id={`treeViewLi${file.path}`}
data-type={file.isDirectory ? 'folder' : 'file'}
data-path={`${file.path}`}
data-id={`treeViewLitreeViewItem${file.path}`}
>
{getIndentLevelDiv(file.path)}
@ -194,7 +188,7 @@ export const FlatTree = (props: FlatTreeProps) => {
<RecursiveItemInput editModeOff={editModeOff} file={file}/>:
<div draggable={true} onDragStart={onDragStart} onDragEnd={onDragEnd} className={`ml-1 pl-2 text-nowrap remixui_leaf ${getFileStateClasses(file)}`} data-label-type={file.isDirectory ? 'folder' : 'file'} data-label-path={`${file.path}`} key={index}>{file.name}
</div>}
</div>)
</li>)
}
return (<>

@ -3,7 +3,7 @@ import { createContext, SyntheticEvent } from 'react'
import { BrowserState } from '../reducers/workspace'
export const FileSystemContext = createContext<{
fs: BrowserState,
fs: any,
plugin: any,
modal:(title: string | JSX.Element, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
dispatchInitWorkspace:() => Promise<void>,

@ -19,6 +19,7 @@ export interface BrowserState {
currentBranch?: string
}[]
files: {[x: string]: Record<string, FileType>}
flatTree: {[x: string]: FileType}
expandPath: string[]
isRequestingDirectory: boolean
isSuccessfulDirectory: boolean
@ -38,6 +39,7 @@ export interface BrowserState {
localhost: {
sharedFolder: string
files: {[x: string]: Record<string, FileType>}
flatTree: {[x: string]: FileType}
expandPath: string[]
isRequestingDirectory: boolean
isSuccessfulDirectory: boolean
@ -73,6 +75,7 @@ export const browserInitialState: BrowserState = {
currentWorkspace: '',
workspaces: [],
files: {},
flatTree: {},
expandPath: [],
isRequestingDirectory: false,
isSuccessfulDirectory: false,
@ -92,6 +95,7 @@ export const browserInitialState: BrowserState = {
localhost: {
sharedFolder: '',
files: {},
flatTree: {},
expandPath: [],
isRequestingDirectory: false,
isSuccessfulDirectory: false,
@ -187,6 +191,7 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
console.log('FETCH_DIRECTORY_SUCCESS', payload)
const startTime = new Date().getTime()
const fd = fetchDirectoryContent(state, payload)
const flatTree = flattenTree(fd, state.browser.expandPath)
const endTime = new Date().getTime()
console.log('fetchDirectoryContent tree', endTime - startTime, fd)
@ -199,6 +204,7 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
state.mode === 'browser'
? fd
: state.browser.files,
flatTree: state.mode === 'browser' ? flatTree : state.browser.flatTree,
isRequestingDirectory: false,
isSuccessfulDirectory: true,
error: null
@ -209,6 +215,7 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
state.mode === 'localhost'
? fetchDirectoryContent(state, payload)
: state.localhost.files,
flatTree: state.mode === 'localhost' ? flatTree : state.localhost.flatTree,
isRequestingDirectory: false,
isSuccessfulDirectory: true,
error: null
@ -259,6 +266,7 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
}
const startTime = new Date().getTime()
const fd = fetchWorkspaceDirectoryContent(state, payload)
const flatTree = flattenTree(fd, state.browser.expandPath)
const endTime = new Date().getTime()
console.log('fetchDirectoryContent tree', endTime - startTime, fd)
@ -270,6 +278,7 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
state.mode === 'browser'
? fd
: state.browser.files,
flatTree: state.mode === 'browser' ? flatTree : state.browser.flatTree,
isRequestingWorkspace: false,
isSuccessfulWorkspace: true,
error: null
@ -278,8 +287,9 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
...state.localhost,
files:
state.mode === 'localhost'
? fetchWorkspaceDirectoryContent(state, payload)
? fd
: state.localhost.files,
flatTree: state.mode === 'localhost' ? flatTree : state.localhost.flatTree,
isRequestingWorkspace: false,
isSuccessfulWorkspace: true,
error: null,
@ -334,14 +344,19 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
case 'FILE_ADDED_SUCCESS': {
const payload = action.payload
const fd = fileAdded(state, payload)
const dir = extractParentFromKey(payload)
console.log('FILE_ADDED_SUCCESS', payload, dir)
const flatTree = flattenTree(fd, state.browser.expandPath)
return {
...state,
browser: {
...state.browser,
files:
state.mode === 'browser'
? fileAdded(state, payload)
? fd
: state.browser.files,
flatTree: state.mode === 'browser' ? flatTree : state.browser.flatTree,
expandPath:
state.mode === 'browser' && !isElectron()
? [...new Set([...state.browser.expandPath, payload])]
@ -351,8 +366,9 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
...state.localhost,
files:
state.mode === 'localhost'
? fileAdded(state, payload)
? fd
: state.localhost.files,
flatTree: state.mode === 'localhost' ? flatTree : state.localhost.flatTree,
expandPath:
state.mode === 'localhost'
? [...new Set([...state.localhost.expandPath, payload])]
@ -363,15 +379,18 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
case 'FOLDER_ADDED_SUCCESS': {
const payload = action.payload
const fd = fetchDirectoryContent(state, payload)
console.log('FOLDER_ADDED_SUCCESS', payload)
const flatTree = flattenTree(fd, state.browser.expandPath)
return {
...state,
browser: {
...state.browser,
files:
state.mode === 'browser'
? fetchDirectoryContent(state, payload)
? fd
: state.browser.files,
flatTree: state.mode === 'browser' ? flatTree : state.browser.flatTree,
expandPath:
state.mode === 'browser' && !isElectron()
? [...new Set([...state.browser.expandPath, payload.folderPath])]
@ -381,8 +400,9 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
...state.localhost,
files:
state.mode === 'localhost'
? fetchDirectoryContent(state, payload)
? fd
: state.localhost.files,
flatTree: state.mode === 'localhost' ? flatTree : state.localhost.flatTree,
expandPath:
state.mode === 'localhost'
? [
@ -398,15 +418,17 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
case 'FILE_REMOVED_SUCCESS': {
const payload = action.payload
const fd = fileRemoved(state, payload)
const flatTree = flattenTree(fd, state.browser.expandPath)
return {
...state,
browser: {
...state.browser,
files:
state.mode === 'browser'
? fileRemoved(state, payload)
? fd
: state.browser.files,
flatTree: state.mode === 'browser' ? flatTree : state.browser.flatTree,
expandPath:
state.mode === 'browser'
? [...state.browser.expandPath.filter((path) => path !== payload)]
@ -416,8 +438,9 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
...state.localhost,
files:
state.mode === 'localhost'
? fileRemoved(state, payload)
? fd
: state.localhost.files,
flatTree: state.mode === 'localhost' ? flatTree : state.localhost.flatTree,
expandPath:
state.mode === 'localhost'
? [...state.browser.expandPath.filter((path) => path !== payload)]
@ -440,22 +463,25 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
case 'ADD_INPUT_FIELD': {
const payload = action.payload
const fd = fetchDirectoryContent(state, payload)
const flatTree = flattenTree(fd, state.browser.expandPath)
return {
...state,
browser: {
...state.browser,
files:
state.mode === 'browser'
? fetchDirectoryContent(state, payload)
: state.browser.files
? fd
: state.browser.files,
flatTree: state.mode === 'browser' ? flatTree : state.browser.flatTree,
},
localhost: {
...state.localhost,
files:
state.mode === 'localhost'
? fetchDirectoryContent(state, payload)
: state.localhost.files
? fd
: state.localhost.files,
flatTree: state.mode === 'localhost' ? flatTree : state.localhost.flatTree,
},
focusEdit: payload.path + '/' + 'blank'
}
@ -463,22 +489,25 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
case 'REMOVE_INPUT_FIELD': {
const payload = action.payload
const fd = removeInputField(state, payload.path)
const flatTree = flattenTree(fd, state.browser.expandPath)
return {
...state,
browser: {
...state.browser,
files:
state.mode === 'browser'
? removeInputField(state, payload.path)
: state.browser.files
? fd
: state.browser.files,
flatTree: state.mode === 'browser' ? flatTree : state.browser.flatTree,
},
localhost: {
...state.localhost,
files:
state.mode === 'localhost'
? removeInputField(state, payload.path)
: state.localhost.files
? fd
: state.localhost.files,
flatTree: state.mode === 'localhost' ? flatTree : state.localhost.flatTree,
},
focusEdit: null
}
@ -495,22 +524,25 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
case 'FILE_RENAMED_SUCCESS': {
const payload = action.payload
const fd = fetchDirectoryContent(state, payload, payload.oldPath)
const flatTree = flattenTree(fd, state.browser.expandPath)
return {
...state,
browser: {
...state.browser,
files:
state.mode === 'browser'
? fetchDirectoryContent(state, payload, payload.oldPath)
: state.browser.files
? fd
: state.browser.files,
flatTree: state.mode === 'browser' ? flatTree : state.browser.flatTree,
},
localhost: {
...state.localhost,
files:
state.mode === 'localhost'
? fetchDirectoryContent(state, payload, payload.oldPath)
: state.localhost.files
? fd
: state.localhost.files,
flatTree: state.mode === 'localhost' ? flatTree : state.localhost.flatTree,
}
}
}
@ -675,15 +707,17 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
case 'SET_EXPAND_PATH': {
const payload = action.payload as string[]
const flatTree = flattenTree(state.browser.files, payload)
return {
...state,
browser: {
...state.browser,
flatTree: state.mode === 'browser' ? flatTree : state.browser.flatTree,
expandPath: payload
},
localhost: {
...state.localhost,
flatTree: state.mode === 'localhost' ? flatTree : state.localhost.flatTree,
expandPath: payload
}
}
@ -867,8 +901,20 @@ export const browserReducer = (state = browserInitialState, action: Actions) =>
}
}
const flattenTree = () =>{
const flattenTree = (files, expandPath: string[]) =>{
const flatTree = {}
const mapChild = (file: FileType) => {
flatTree[file.path] = file
expandPath && expandPath.includes(file.path) &&
file.child && Object.keys(file.child).map((key) => {
mapChild(file.child[key])
})
}
files && files[ROOT_PATH] && Object.keys(files[ROOT_PATH]).map((key) => {
mapChild(files[ROOT_PATH][key])
})
console.log('flat tree', flatTree)
return flatTree
}
const fileAdded = (
@ -878,6 +924,7 @@ const fileAdded = (
let files =
state.mode === 'browser' ? state.browser.files : state.localhost.files
const _path = splitPath(state, path)
const childPath = _path.slice(0, _path.length - 1)
files = _.setWith(
files,
@ -890,6 +937,18 @@ const fileAdded = (
},
Object
)
const prevFiles = _.get(files, childPath)
const sortedFiles = fileKeySort(prevFiles)
files = _.setWith(
files,
childPath,
{
...sortedFiles
},
Object
)
return files
}
@ -952,7 +1011,7 @@ const fetchDirectoryContent = (
return state.mode === 'browser'
? state.browser.files
: state[state.mode].files
fileKeySort(payload.fileTree)
payload.fileTree = fileKeySort(payload.fileTree)
if (state.mode === 'browser') {
if (payload.path === ROOT_PATH) {
let files = normalize(payload.fileTree, ROOT_PATH, payload.type)
@ -1040,6 +1099,8 @@ const fetchWorkspaceDirectoryContent = (
state: BrowserState,
payload: {fileTree; path: string}
): {[x: string]: Record<string, FileType>} => {
console.log('fetchWorkspaceDirectoryContent', payload.fileTree)
payload.fileTree = fileKeySort(payload.fileTree)
const files = normalize(payload.fileTree, ROOT_PATH)
return {[ROOT_PATH]: files}

@ -1064,6 +1064,7 @@ export function Workspace() {
contextMenuItems={global.fs.browser.contextMenu.registeredMenuItems}
removedContextMenuItems={global.fs.browser.contextMenu.removedMenuItems}
files={global.fs.browser.files}
flatTree={global.fs.browser.flatTree}
workspaceState={state}
expandPath={global.fs.browser.expandPath}
focusEdit={global.fs.focusEdit}
@ -1122,6 +1123,7 @@ export function Workspace() {
contextMenuItems={global.fs.localhost.contextMenu.registeredMenuItems}
removedContextMenuItems={global.fs.localhost.contextMenu.removedMenuItems}
files={global.fs.localhost.files}
flatTree={global.fs.browser.flatTree}
fileState={[]}
workspaceState={state}
expandPath={global.fs.localhost.expandPath}

@ -85,6 +85,7 @@ export interface FileExplorerProps {
contextMenuItems: MenuItems,
removedContextMenuItems: MenuItems,
files: { [x: string]: Record<string, FileType> },
flatTree: {[x: string]: FileType},
workspaceState: WorkSpaceState,
fileState: fileDecoration[],
expandPath: string[],

@ -124,13 +124,13 @@ export const contextMenuActions: MenuItems = [{
platform: appPlatformTypes.web
}]
export const fileKeySort = (children: any): string[] => {
const directories = Object.keys(children).filter((key: string) => children[key].isDirectory)
export const fileKeySort = (fileTree: any) => {
const directories = Object.keys(fileTree).filter((key: string) => fileTree[key].isDirectory)
// sort case insensitive
directories.sort((a: string, b: string) => a.toLowerCase().localeCompare(b.toLowerCase()))
const fileKeys = Object.keys(children).filter((key: string) => !children[key].isDirectory)
const fileKeys = Object.keys(fileTree).filter((key: string) => !fileTree[key].isDirectory)
// sort case insensitive
fileKeys.sort((a: string, b: string) => a.toLowerCase().localeCompare(b.toLowerCase()))
@ -139,5 +139,10 @@ export const fileKeySort = (children: any): string[] => {
const keys = [...directories, ...fileKeys]
console.log('sorted keys', keys)
return keys
// rebuild the fileTree using the keys
const newFileTree = {}
keys.forEach((key: string) => {
newFileTree[key] = fileTree[key]
})
return newFileTree
}

Loading…
Cancel
Save