Pass global as props

pull/1575/head
ioedeveloper 3 years ago
parent 079cd3627f
commit 4ba2237822
  1. 123
      libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
  2. 36
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  3. 22
      libs/remix-ui/workspace/src/lib/types/index.ts

@ -1,10 +1,8 @@
import React, { useEffect, useState, useRef, useContext } from 'react' // eslint-disable-line import React, { useEffect, useState, useContext, SyntheticEvent } from 'react' // eslint-disable-line
// import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd' // eslint-disable-line
import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-line import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-line
import { FileExplorerMenu } from './file-explorer-menu' // eslint-disable-line import { FileExplorerMenu } from './file-explorer-menu' // eslint-disable-line
import { FileExplorerContextMenu } from './file-explorer-context-menu' // eslint-disable-line import { FileExplorerContextMenu } from './file-explorer-context-menu' // eslint-disable-line
import { FileExplorerProps, MenuItems, FileExplorerState } from '../types' import { FileExplorerProps, MenuItems, FileExplorerState } from '../types'
import { FileSystemContext } from '../contexts'
import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel' import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel'
import { contextMenuActions } from '../utils' import { contextMenuActions } from '../utils'
@ -14,7 +12,7 @@ import { checkSpecialChars, extractNameFromKey, extractParentFromKey, joinPath }
import { FileRender } from './file-render' import { FileRender } from './file-render'
export const FileExplorer = (props: FileExplorerProps) => { export const FileExplorer = (props: FileExplorerProps) => {
const { name, contextMenuItems, externalUploads, removedContextMenuItems, files } = props const { name, contextMenuItems, removedContextMenuItems, files } = props
const [state, setState] = useState<FileExplorerState>({ const [state, setState] = useState<FileExplorerState>({
ctrlKey: false, ctrlKey: false,
newFileName: '', newFileName: '',
@ -38,7 +36,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
copyElement: [] copyElement: []
}) })
const [canPaste, setCanPaste] = useState(false) const [canPaste, setCanPaste] = useState(false)
const global = useContext(FileSystemContext)
useEffect(() => { useEffect(() => {
setState(prevState => { setState(prevState => {
@ -66,12 +63,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
}, [props.focusEdit]) }, [props.focusEdit])
useEffect(() => {
if (externalUploads) {
uploadFile(externalUploads)
}
}, [externalUploads])
useEffect(() => { useEffect(() => {
const keyPressHandler = (e: KeyboardEvent) => { const keyPressHandler = (e: KeyboardEvent) => {
if (e.shiftKey) { if (e.shiftKey) {
@ -155,32 +146,32 @@ export const FileExplorer = (props: FileExplorerProps) => {
const createNewFile = async (newFilePath: string) => { const createNewFile = async (newFilePath: string) => {
try { try {
global.dispatchCreateNewFile(newFilePath, props.name) props.dispatchCreateNewFile(newFilePath, props.name)
} catch (error) { } catch (error) {
return global.modal('File Creation Failed', typeof error === 'string' ? error : error.message, 'Close', async () => {}) return props.modal('File Creation Failed', typeof error === 'string' ? error : error.message, 'Close', async () => {})
} }
} }
const createNewFolder = async (newFolderPath: string) => { const createNewFolder = async (newFolderPath: string) => {
try { try {
global.dispatchCreateNewFolder(newFolderPath, props.name) props.dispatchCreateNewFolder(newFolderPath, props.name)
} catch (e) { } catch (e) {
return global.modal('Folder Creation Failed', typeof e === 'string' ? e : e.message, 'Close', async () => {}) return props.modal('Folder Creation Failed', typeof e === 'string' ? e : e.message, 'Close', async () => {})
} }
} }
const deletePath = async (path: string[]) => { const deletePath = async (path: string[]) => {
if (global.fs.readonly) return global.toast('cannot delete file. ' + name + ' is a read only explorer') if (props.readonly) return props.toast('cannot delete file. ' + name + ' is a read only explorer')
if (!Array.isArray(path)) path = [path] if (!Array.isArray(path)) path = [path]
global.modal(`Delete ${path.length > 1 ? 'items' : 'item'}`, deleteMessage(path), 'OK', () => { global.dispatchDeletePath(path) }, 'Cancel', () => {}) props.modal(`Delete ${path.length > 1 ? 'items' : 'item'}`, deleteMessage(path), 'OK', () => { props.dispatchDeletePath(path) }, 'Cancel', () => {})
} }
const renamePath = async (oldPath: string, newPath: string) => { const renamePath = async (oldPath: string, newPath: string) => {
try { try {
global.dispatchRenamePath(oldPath, newPath) props.dispatchRenamePath(oldPath, newPath)
} catch (error) { } catch (error) {
global.modal('Rename File Failed', 'Unexpected error while renaming: ' + typeof error === 'string' ? error : error.message, 'Close', async () => {}) props.modal('Rename File Failed', 'Unexpected error while renaming: ' + typeof error === 'string' ? error : error.message, 'Close', async () => {})
} }
} }
@ -191,75 +182,75 @@ export const FileExplorer = (props: FileExplorerProps) => {
setState(prevState => { setState(prevState => {
return { ...prevState, expandPath } return { ...prevState, expandPath }
}) })
global.dispatchUploadFile(target, parentFolder) props.dispatchUploadFile(target, parentFolder)
} }
const copyFile = (src: string, dest: string) => { const copyFile = (src: string, dest: string) => {
try { try {
global.dispatchCopyFile(src, dest) props.dispatchCopyFile(src, dest)
} catch (error) { } catch (error) {
global.modal('Copy File Failed', 'Unexpected error while copying file: ' + src, 'Close', async () => {}) props.modal('Copy File Failed', 'Unexpected error while copying file: ' + src, 'Close', async () => {})
} }
} }
const copyFolder = (src: string, dest: string) => { const copyFolder = (src: string, dest: string) => {
try { try {
global.dispatchCopyFolder(src, dest) props.dispatchCopyFolder(src, dest)
} catch (error) { } catch (error) {
global.modal('Copy Folder Failed', 'Unexpected error while copying folder: ' + src, 'Close', async () => {}) props.modal('Copy Folder Failed', 'Unexpected error while copying folder: ' + src, 'Close', async () => {})
} }
} }
const publishToGist = (path?: string, type?: string) => { const publishToGist = (path?: string, type?: string) => {
global.modal('Create a public gist', `Are you sure you want to anonymously publish all your files in the ${name} workspace as a public gist on github.com?`, 'OK', () => toGist(path, type), 'Cancel', () => {}) props.modal('Create a public gist', `Are you sure you want to anonymously publish all your files in the ${name} workspace as a public gist on github.com?`, 'OK', () => toGist(path, type), 'Cancel', () => {})
} }
const pushChangesToGist = (path?: string, type?: string) => { const pushChangesToGist = (path?: string, type?: string) => {
global.modal('Create a public gist', 'Are you sure you want to push changes to remote gist file on github.com?', 'OK', () => toGist(path, type), 'Cancel', () => {}) props.modal('Create a public gist', 'Are you sure you want to push changes to remote gist file on github.com?', 'OK', () => toGist(path, type), 'Cancel', () => {})
} }
const publishFolderToGist = (path?: string, type?: string) => { const publishFolderToGist = (path?: string, type?: string) => {
global.modal('Create a public gist', `Are you sure you want to anonymously publish all your files in the ${path} folder as a public gist on github.com?`, 'OK', () => toGist(path, type), 'Cancel', () => {}) props.modal('Create a public gist', `Are you sure you want to anonymously publish all your files in the ${path} folder as a public gist on github.com?`, 'OK', () => toGist(path, type), 'Cancel', () => {})
} }
const publishFileToGist = (path?: string, type?: string) => { const publishFileToGist = (path?: string, type?: string) => {
global.modal('Create a public gist', `Are you sure you want to anonymously publish ${path} file as a public gist on github.com?`, 'OK', () => toGist(path, type), 'Cancel', () => {}) props.modal('Create a public gist', `Are you sure you want to anonymously publish ${path} file as a public gist on github.com?`, 'OK', () => toGist(path, type), 'Cancel', () => {})
} }
const toGist = (path?: string, type?: string) => { const toGist = (path?: string, type?: string) => {
global.dispatchPublishToGist(path, type) props.dispatchPublishToGist(path, type)
} }
const runScript = async (path: string) => { const runScript = async (path: string) => {
try { try {
global.dispatchRunScript(path) props.dispatchRunScript(path)
} catch (error) { } catch (error) {
global.toast('Run script failed') props.toast('Run script failed')
} }
} }
const emitContextMenuEvent = (cmd: customAction) => { const emitContextMenuEvent = (cmd: customAction) => {
try { try {
global.dispatchEmitContextMenuEvent(cmd) props.dispatchEmitContextMenuEvent(cmd)
} catch (error) { } catch (error) {
global.toast(error) props.toast(error)
} }
} }
const handleClickFile = (path: string, type: 'folder' | 'file' | 'gist') => { const handleClickFile = (path: string, type: 'folder' | 'file' | 'gist') => {
path = path.indexOf(props.name + '/') === 0 ? path.replace(props.name + '/', '') : path path = path.indexOf(props.name + '/') === 0 ? path.replace(props.name + '/', '') : path
if (!state.ctrlKey) { if (!state.ctrlKey) {
global.dispatchHandleClickFile(path, type) props.dispatchHandleClickFile(path, type)
} else { } else {
if (props.focusElement.findIndex(item => item.key === path) !== -1) { if (props.focusElement.findIndex(item => item.key === path) !== -1) {
const focusElement = props.focusElement.filter(item => item.key !== path) const focusElement = props.focusElement.filter(item => item.key !== path)
global.dispatchSetFocusElement(focusElement) props.dispatchSetFocusElement(focusElement)
} else { } else {
const nonRootFocus = props.focusElement.filter((el) => { return !(el.key === '' && el.type === 'folder') }) const nonRootFocus = props.focusElement.filter((el) => { return !(el.key === '' && el.type === 'folder') })
nonRootFocus.push({ key: path, type }) nonRootFocus.push({ key: path, type })
global.dispatchSetFocusElement(nonRootFocus) props.dispatchSetFocusElement(nonRootFocus)
} }
} }
} }
@ -269,24 +260,24 @@ export const FileExplorer = (props: FileExplorerProps) => {
if (props.focusElement.findIndex(item => item.key === path) !== -1) { if (props.focusElement.findIndex(item => item.key === path) !== -1) {
const focusElement = props.focusElement.filter(item => item.key !== path) const focusElement = props.focusElement.filter(item => item.key !== path)
global.dispatchSetFocusElement(focusElement) props.dispatchSetFocusElement(focusElement)
} else { } else {
const nonRootFocus = props.focusElement.filter((el) => { return !(el.key === '' && el.type === 'folder') }) const nonRootFocus = props.focusElement.filter((el) => { return !(el.key === '' && el.type === 'folder') })
nonRootFocus.push({ key: path, type }) nonRootFocus.push({ key: path, type })
global.dispatchSetFocusElement(nonRootFocus) props.dispatchSetFocusElement(nonRootFocus)
} }
} else { } else {
let expandPath = [] let expandPath = []
if (!state.expandPath.includes(path)) { if (!state.expandPath.includes(path)) {
expandPath = [...new Set([...state.expandPath, path])] expandPath = [...new Set([...state.expandPath, path])]
global.dispatchFetchDirectory(path) props.dispatchFetchDirectory(path)
} else { } else {
expandPath = [...new Set(state.expandPath.filter(key => key && (typeof key === 'string') && !key.startsWith(path)))] expandPath = [...new Set(state.expandPath.filter(key => key && (typeof key === 'string') && !key.startsWith(path)))]
} }
global.dispatchSetFocusElement([{ key: path, type }]) props.dispatchSetFocusElement([{ key: path, type }])
setState(prevState => { setState(prevState => {
return { ...prevState, expandPath } return { ...prevState, expandPath }
}) })
@ -307,7 +298,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
const editModeOn = (path: string, type: string, isNew: boolean = false) => { const editModeOn = (path: string, type: string, isNew: boolean = false) => {
if (global.fs.readonly) return if (props.readonly) return
setState(prevState => { setState(prevState => {
return { ...prevState, focusEdit: { ...prevState.focusEdit, element: path, isNew, type } } return { ...prevState, focusEdit: { ...prevState.focusEdit, element: path, isNew, type } }
}) })
@ -319,7 +310,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
if (!content || (content.trim() === '')) { if (!content || (content.trim() === '')) {
if (state.focusEdit.isNew) { if (state.focusEdit.isNew) {
global.dispatchRemoveInputField(parentFolder) props.dispatchRemoveInputField(parentFolder)
setState(prevState => { setState(prevState => {
return { ...prevState, focusEdit: { element: null, isNew: false, type: '', lastEdit: '' } } return { ...prevState, focusEdit: { element: null, isNew: false, type: '', lastEdit: '' } }
}) })
@ -337,20 +328,20 @@ export const FileExplorer = (props: FileExplorerProps) => {
}) })
} }
if (checkSpecialChars(content)) { if (checkSpecialChars(content)) {
global.modal('Validation Error', 'Special characters are not allowed', 'OK', () => {}) props.modal('Validation Error', 'Special characters are not allowed', 'OK', () => {})
} else { } else {
if (state.focusEdit.isNew) { if (state.focusEdit.isNew) {
if (hasReservedKeyword(content)) { if (hasReservedKeyword(content)) {
global.dispatchRemoveInputField(parentFolder) props.dispatchRemoveInputField(parentFolder)
global.modal('Reserved Keyword', `File name contains remix reserved keywords. '${content}'`, 'Close', () => {}) props.modal('Reserved Keyword', `File name contains remix reserved keywords. '${content}'`, 'Close', () => {})
} else { } else {
state.focusEdit.type === 'file' ? createNewFile(joinPath(parentFolder, content)) : createNewFolder(joinPath(parentFolder, content)) state.focusEdit.type === 'file' ? createNewFile(joinPath(parentFolder, content)) : createNewFolder(joinPath(parentFolder, content))
global.dispatchRemoveInputField(parentFolder) props.dispatchRemoveInputField(parentFolder)
} }
} else { } else {
if (hasReservedKeyword(content)) { if (hasReservedKeyword(content)) {
// editRef.current.textContent = state.focusEdit.lastEdit // editRef.current.textContent = state.focusEdit.lastEdit
global.modal('Reserved Keyword', `File name contains remix reserved keywords. '${content}'`, 'Close', () => {}) props.modal('Reserved Keyword', `File name contains remix reserved keywords. '${content}'`, 'Close', () => {})
} else { } else {
const oldPath: string = state.focusEdit.element const oldPath: string = state.focusEdit.element
const oldName = extractNameFromKey(oldPath) const oldName = extractNameFromKey(oldPath)
@ -371,7 +362,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
if (!parentFolder) parentFolder = getFocusedFolder() if (!parentFolder) parentFolder = getFocusedFolder()
const expandPath = [...new Set([...state.expandPath, parentFolder])] const expandPath = [...new Set([...state.expandPath, parentFolder])]
await global.dispatchAddInputField(parentFolder, 'file') await props.dispatchAddInputField(parentFolder, 'file')
setState(prevState => { setState(prevState => {
return { ...prevState, expandPath } return { ...prevState, expandPath }
}) })
@ -383,7 +374,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
else if ((parentFolder.indexOf('.sol') !== -1) || (parentFolder.indexOf('.js') !== -1)) parentFolder = extractParentFromKey(parentFolder) else if ((parentFolder.indexOf('.sol') !== -1) || (parentFolder.indexOf('.js') !== -1)) parentFolder = extractParentFromKey(parentFolder)
const expandPath = [...new Set([...state.expandPath, parentFolder])] const expandPath = [...new Set([...state.expandPath, parentFolder])]
await global.dispatchAddInputField(parentFolder, 'folder') await props.dispatchAddInputField(parentFolder, 'folder')
setState(prevState => { setState(prevState => {
return { ...prevState, expandPath } return { ...prevState, expandPath }
}) })
@ -395,7 +386,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
return { ...prevState, copyElement: [{ key: path, type }] } return { ...prevState, copyElement: [{ key: path, type }] }
}) })
setCanPaste(true) setCanPaste(true)
global.toast(`Copied to clipboard ${path}`) props.toast(`Copied to clipboard ${path}`)
} }
const handlePasteClick = (dest: string, destType: string) => { const handlePasteClick = (dest: string, destType: string) => {
@ -416,27 +407,29 @@ export const FileExplorer = (props: FileExplorerProps) => {
) )
} }
const handleFileExplorerMenuClick = (e: SyntheticEvent) => {
e.stopPropagation()
if (e && (e.target as any).getAttribute('data-id') === 'fileExplorerUploadFileuploadFile') return // we don't want to let propagate the input of type file
if (e && (e.target as any).getAttribute('data-id') === 'fileExplorerFileUpload') return // we don't want to let propagate the input of type file
let expandPath = []
if (!state.expandPath.includes(props.name)) {
expandPath = [props.name, ...new Set([...state.expandPath])]
} else {
expandPath = [...new Set(state.expandPath.filter(key => key && (typeof key === 'string') && !key.startsWith(props.name)))]
}
setState(prevState => {
return { ...prevState, expandPath }
})
}
return ( return (
<div> <div>
<TreeView id='treeView'> <TreeView id='treeView'>
<TreeViewItem id="treeViewItem" <TreeViewItem id="treeViewItem"
controlBehaviour={true} controlBehaviour={true}
label={ label={
<div onClick={(e) => { <div onClick={handleFileExplorerMenuClick}>
e.stopPropagation()
if (e && (e.target as any).getAttribute('data-id') === 'fileExplorerUploadFileuploadFile') return // we don't want to let propagate the input of type file
if (e && (e.target as any).getAttribute('data-id') === 'fileExplorerFileUpload') return // we don't want to let propagate the input of type file
let expandPath = []
if (!state.expandPath.includes(props.name)) {
expandPath = [props.name, ...new Set([...state.expandPath])]
} else {
expandPath = [...new Set(state.expandPath.filter(key => key && (typeof key === 'string') && !key.startsWith(props.name)))]
}
setState(prevState => {
return { ...prevState, expandPath }
})
}}>
<FileExplorerMenu <FileExplorerMenu
title={''} title={''}
menuItems={props.menuItems} menuItems={props.menuItems}

@ -202,6 +202,24 @@ export function Workspace (props: WorkspaceProps) {
expandPath={global.fs.browser.expandPath} expandPath={global.fs.browser.expandPath}
focusEdit={global.fs.focusEdit} focusEdit={global.fs.focusEdit}
focusElement={global.fs.focusElement} focusElement={global.fs.focusElement}
dispatchCreateNewFile={global.dispatchCreateNewFile}
modal={global.modal}
dispatchCreateNewFolder={global.dispatchCreateNewFolder}
readonly={global.fs.readonly}
toast={global.toast}
dispatchDeletePath={global.dispatchDeletePath}
dispatchRenamePath={global.dispatchRenamePath}
dispatchUploadFile={global.dispatchUploadFile}
dispatchCopyFile={global.dispatchCopyFile}
dispatchCopyFolder={global.dispatchCopyFolder}
dispatchPublishToGist={global.dispatchPublishToGist}
dispatchRunScript={global.dispatchRunScript}
dispatchEmitContextMenuEvent={global.dispatchEmitContextMenuEvent}
dispatchHandleClickFile={global.dispatchHandleClickFile}
dispatchSetFocusElement={global.dispatchSetFocusElement}
dispatchFetchDirectory={global.dispatchFetchDirectory}
dispatchRemoveInputField={global.dispatchRemoveInputField}
dispatchAddInputField={global.dispatchAddInputField}
/> />
} }
</div> </div>
@ -218,6 +236,24 @@ export function Workspace (props: WorkspaceProps) {
expandPath={global.fs.localhost.expandPath} expandPath={global.fs.localhost.expandPath}
focusEdit={global.fs.focusEdit} focusEdit={global.fs.focusEdit}
focusElement={global.fs.focusElement} focusElement={global.fs.focusElement}
dispatchCreateNewFile={global.dispatchCreateNewFile}
modal={global.modal}
dispatchCreateNewFolder={global.dispatchCreateNewFolder}
readonly={global.fs.readonly}
toast={global.toast}
dispatchDeletePath={global.dispatchDeletePath}
dispatchRenamePath={global.dispatchRenamePath}
dispatchUploadFile={global.dispatchUploadFile}
dispatchCopyFile={global.dispatchCopyFile}
dispatchCopyFolder={global.dispatchCopyFolder}
dispatchPublishToGist={global.dispatchPublishToGist}
dispatchRunScript={global.dispatchRunScript}
dispatchEmitContextMenuEvent={global.dispatchEmitContextMenuEvent}
dispatchHandleClickFile={global.dispatchHandleClickFile}
dispatchSetFocusElement={global.dispatchSetFocusElement}
dispatchFetchDirectory={global.dispatchFetchDirectory}
dispatchRemoveInputField={global.dispatchRemoveInputField}
dispatchAddInputField={global.dispatchAddInputField}
/> />
} }
</div> </div>

@ -57,12 +57,28 @@ export interface FileExplorerProps {
menuItems?: string[], menuItems?: string[],
contextMenuItems: MenuItems, contextMenuItems: MenuItems,
removedContextMenuItems: MenuItems, removedContextMenuItems: MenuItems,
displayInput?: boolean,
externalUploads?: EventTarget & HTMLInputElement,
files: { [x: string]: Record<string, FileType> }, files: { [x: string]: Record<string, FileType> },
expandPath: string[], expandPath: string[],
focusEdit: string, focusEdit: string,
focusElement: { key: string, type: 'file' | 'folder' | 'gist' }[] focusElement: { key: string, type: 'file' | 'folder' | 'gist' }[],
dispatchCreateNewFile: (path: string, rootDir: string) => Promise<void>,
modal:(title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
dispatchCreateNewFolder: (path: string, rootDir: string) => Promise<void>,
readonly: boolean,
toast: (toasterMsg: string) => void,
dispatchDeletePath: (path: string[]) => Promise<void>,
dispatchRenamePath: (oldPath: string, newPath: string) => Promise<void>,
dispatchUploadFile: (target?: React.SyntheticEvent, targetFolder?: string) => Promise<void>,
dispatchCopyFile: (src: string, dest: string) => Promise<void>,
dispatchCopyFolder: (src: string, dest: string) => Promise<void>,
dispatchRunScript: (path: string) => Promise<void>,
dispatchPublishToGist: (path?: string, type?: string) => Promise<void>,
dispatchEmitContextMenuEvent: (cmd: customAction) => Promise<void>,
dispatchHandleClickFile: (path: string, type: 'file' | 'folder' | 'gist') => Promise<void>,
dispatchSetFocusElement: (elements: { key: string, type: 'file' | 'folder' | 'gist' }[]) => Promise<void>,
dispatchFetchDirectory:(path: string) => Promise<void>,
dispatchRemoveInputField:(path: string) => Promise<void>,
dispatchAddInputField:(path: string, type: 'file' | 'folder') => Promise<void>
} }
export interface FileExplorerMenuProps { export interface FileExplorerMenuProps {

Loading…
Cancel
Save