Include context menu

pull/668/head
ioedeveloper 4 years ago
parent 9b0e58c41e
commit 8ffadf3742
  1. 25
      libs/remix-ui/file-explorer/src/lib/file-explorer-menu.tsx
  2. 139
      libs/remix-ui/file-explorer/src/lib/file-explorer.tsx
  3. 1
      libs/remix-ui/file-explorer/src/lib/types/index.ts
  4. 3
      libs/remix-ui/tree-view/src/types/index.ts

@ -70,29 +70,6 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
})
}, [])
const createNewFile = (parentFolder = 'browser/Folder 2') => {
// const self = this
// modalDialogCustom.prompt('Create new file', 'File Name (e.g Untitled.sol)', 'Untitled.sol', (input) => {
// if (!input) input = 'New file'
// get filename from state (state.newFileName)
const fileManager = props.fileManager
const newFileName = parentFolder + '/' + 'unnamed' + Math.floor(Math.random() * 101)
helper.createNonClashingName(newFileName, props.files, async (error, newName) => {
// if (error) return tooltip('Failed to create file ' + newName + ' ' + error)
if (error) return
const createFile = await fileManager.writeFile(newName, '')
if (!createFile) {
// tooltip('Failed to create file ' + newName)
} else {
props.addFile(parentFolder, newFileName)
await fileManager.open(newName)
}
})
// }, null, true)
}
const uploadFile = (target) => {
// TODO The file explorer is merely a view on the current state of
// the files module. Please ask the user here if they want to overwrite
@ -267,7 +244,7 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
onClick={(e) => {
e.stopPropagation()
if (action === 'createNewFile') {
createNewFile()
props.createNewFile()
} else if (action === 'publishToGist') {
publishToGist()
} else {

@ -3,12 +3,14 @@ import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-l
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd' // eslint-disable-line
import { FileExplorerMenu } from './file-explorer-menu' // eslint-disable-line
import { FileExplorerProps, File } from './types'
import * as helper from '../../../../../apps/remix-ide/src/lib/helper'
import './css/file-explorer.css'
export const FileExplorer = (props: FileExplorerProps) => {
const { files, name, registry, plugin } = props
const containerRef = useRef(null)
const contextMenuRef = useRef({})
const [state, setState] = useState({
focusElement: [],
focusPath: null,
@ -16,7 +18,13 @@ export const FileExplorer = (props: FileExplorerProps) => {
fileManager: null,
accessToken: null,
ctrlKey: false,
newFileName: ''
newFileName: '',
actions: [],
focusContext: {
element: null,
x: null,
y: null
}
})
useEffect(() => {
@ -25,9 +33,25 @@ export const FileExplorer = (props: FileExplorerProps) => {
const config = registry.get('config').api
const accessToken = config.get('settings/gist-access-token')
const files = await fetchDirectoryContent(name)
const actions = [{
name: 'New File',
type: ['folder']
}, {
name: 'New Folder',
type: ['folder']
}, {
name: 'Rename',
type: ['file', 'folder']
}, {
name: 'Delete',
type: ['file', 'folder']
}, {
name: 'Push changes to gist',
type: []
}]
setState(prevState => {
return { ...prevState, fileManager, accessToken, files }
return { ...prevState, fileManager, accessToken, files, actions }
})
})()
}, [])
@ -91,6 +115,35 @@ export const FileExplorer = (props: FileExplorerProps) => {
return keyPath[keyPath.length - 1]
}
const extractParentFromKey = (key: string):string => {
const keyPath = key.split('/')
return keyPath.pop()
}
const createNewFile = (parentFolder?: string) => {
if (!parentFolder) parentFolder = extractParentFromKey(state.focusElement[0])
// const self = this
// modalDialogCustom.prompt('Create new file', 'File Name (e.g Untitled.sol)', 'Untitled.sol', (input) => {
// if (!input) input = 'New file'
const fileManager = state.fileManager
const newFileName = parentFolder + '/' + 'unnamed' + Math.floor(Math.random() * 101) // get filename from state (state.newFileName)
helper.createNonClashingName(newFileName, props.files, async (error, newName) => {
// if (error) return tooltip('Failed to create file ' + newName + ' ' + error)
if (error) return
const createFile = await fileManager.writeFile(newName, '')
if (!createFile) {
// tooltip('Failed to create file ' + newName)
} else {
addFile(parentFolder, newFileName)
await fileManager.open(newName)
}
})
// }, null, true)
}
const addFile = async (parentFolder: string, newFileName: string) => {
if (parentFolder === name) {
setState(prevState => {
@ -181,7 +234,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
// }
// }
const label = (data) => {
const label = (data: File) => {
return (
<div className='remixui_items'>
<span
@ -197,6 +250,33 @@ export const FileExplorer = (props: FileExplorerProps) => {
)
}
const contextMenu = (actions: { name: string, type: string[] }[], index: number) => {
const menu = actions.map((item) => {
return <li
id={`menuitem${item.name.toLowerCase()}`}
className='remixui_liitem'
onClick={() => {
if (item.name === 'Create File') {
createNewFile()
}
setState(prevState => {
return { ...prevState, focusContext: { element: null, x: null, y: null } }
})
}}>{item.name}</li>
})
return (
<div
id="menuItemsContainer"
className="p-1 remixui_container bg-light shadow border"
style={{ left: state.focusContext.x, top: state.focusContext.y }}
ref={ref => { contextMenuRef.current[index] = ref }}
>
<ul id='menuitems'>{menu}</ul>
</div>
)
}
const onDragEnd = result => {
}
@ -206,7 +286,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
setState(prevState => {
return { ...prevState, focusElement: [path] }
})
containerRef.current.focus()
}
const handleClickFolder = async (path) => {
@ -229,7 +308,18 @@ export const FileExplorer = (props: FileExplorerProps) => {
}
}
const renderFiles = (file, index) => {
const handleContextMenuFile = (pageX, pageY, label) => {
const menuItemsContainer = contextMenuRef.current
const boundary = menuItemsContainer.getBoundingClientRect()
if (boundary.bottom > (window.innerHeight || document.documentElement.clientHeight)) {
menuItemsContainer.style.position = 'absolute'
menuItemsContainer.style.bottom = '10px'
menuItemsContainer.style.top = null
}
}
const renderFiles = (file: File, index: number) => {
if (file.isDirectory) {
return (
<Droppable droppableId={file.path} key={index}>
@ -248,6 +338,9 @@ export const FileExplorer = (props: FileExplorerProps) => {
}}
labelClass={ state.focusElement.findIndex(item => item === file.path) !== -1 ? 'bg-secondary' : '' }
controlBehaviour={ state.ctrlKey }
onContextMenu={(e) => {
e.preventDefault()
}}
>
{
file.child ? <TreeView id={`treeView${file.path}`} key={index}>{
@ -266,20 +359,27 @@ export const FileExplorer = (props: FileExplorerProps) => {
return (
<Draggable draggableId={file.path} index={index} key={index}>
{(provided) => (
<TreeViewItem
{...provided.draggableProps}
{...provided.dragHandleProps}
innerRef={provided.innerRef}
id={`treeViewItem${file.path}`}
key={index}
label={label(file)}
onClick={(e) => {
e.stopPropagation()
handleClickFile(file.path)
}}
icon='fa fa-file'
labelClass={ state.focusElement.findIndex(item => item === file.path) !== -1 ? 'bg-secondary' : '' }
/>
<>
<TreeViewItem
{...provided.draggableProps}
{...provided.dragHandleProps}
innerRef={provided.innerRef}
id={`treeViewItem${file.path}`}
key={index}
label={label(file)}
onClick={(e) => {
e.stopPropagation()
handleClickFile(file.path)
}}
onContextMenu={(e) => {
e.preventDefault()
handleContextMenuFile()
}}
icon='fa fa-file'
labelClass={ state.focusElement.findIndex(item => item === file.path) !== -1 ? 'bg-secondary' : '' }
/>
{ contextMenu(state.actions.filter(item => item.type.findIndex(name => name === 'file') !== -1), index) }
</>
)}
</Draggable>
)
@ -310,6 +410,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
title={name}
menuItems={props.menuItems}
addFile={addFile}
createNewFile={createNewFile}
files={props.files}
fileManager={state.fileManager}
accessToken={state.accessToken}

@ -19,6 +19,7 @@ export interface FileExplorerMenuProps {
menuItems: string[],
fileManager: any,
addFile: (parent: string, fileName: string) => void,
createNewFile: () => void,
files: any,
accessToken: string
}

@ -15,5 +15,6 @@ export interface TreeViewItemProps {
icon?: string,
labelClass?: string,
controlBehaviour?: boolean
innerRef?: any
innerRef?: any,
onContextMenu?: (...args: any) => void
}

Loading…
Cancel
Save