diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js
index 08d4602aa5..7ec0d7042b 100644
--- a/apps/remix-ide/src/app/panels/file-panel.js
+++ b/apps/remix-ide/src/app/panels/file-panel.js
@@ -94,20 +94,20 @@ module.exports = class Filepanel extends ViewPlugin {
-
+ /> */}
diff --git a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx
index 9ce6041200..c851a10e84 100644
--- a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx
+++ b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx
@@ -5,29 +5,76 @@ import { FileExplorerProps } from './types'
import './css/file-explorer.css'
-function extractData (value, tree, key) {
- const newValue = {}
- let isFile = false
-
- Object.keys(value).filter((x) => {
- if (x === '/content') isFile = true
- if (x[0] !== '/') return true
- }).forEach((x) => { newValue[x] = value[x] })
-
- return {
- path: (tree || {}).path ? tree.path + '/' + key : key,
- children: isFile ? undefined
- : value instanceof Array ? value.map((item, index) => ({
- key: index, value: item
- })) : value instanceof Object ? Object.keys(value).map(subkey => ({
- key: subkey, value: value[subkey]
- })) : undefined
+export const FileExplorer = (props: FileExplorerProps) => {
+ const { files, name, registry } = props
+ 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
+ // a file and then just use `files.add`. The file explorer will
+ // pick that up via the 'fileAdded' event from the files module.
+
+ [...target.files].forEach((file) => {
+ const files = props.files
+
+ function loadFile () {
+ const fileReader = new FileReader()
+
+ fileReader.onload = async function (event) {
+ if (helper.checkSpecialChars(file.name)) {
+ // modalDialogCustom.alert('Special characters are not allowed')
+ return
+ }
+ const success = await files.set(name, event.target.result)
+
+ if (!success) {
+ // modalDialogCustom.alert('Failed to create file ' + name)
+ } else {
+ // self.events.trigger('focus', [name])
+ }
+ }
+ fileReader.readAsText(file)
+ }
+ const name = files.type + '/' + file.name
+
+ files.exists(name, (error, exist) => {
+ if (error) console.log(error)
+ if (!exist) {
+ loadFile()
+ } else {
+ // modalDialogCustom.confirm('Confirm overwrite', `The file ${name} already exists! Would you like to overwrite it?`, () => { loadFile() })
+ }
+ })
+ })
+ }
+ const publishToGist = () => {
+ // modalDialogCustom.confirm(
+ // 'Create a public gist',
+ // 'Are you sure you want to publish all your files in browser directory anonymously as a public gist on github.com? Note: this will not include directories.',
+ // () => { this.toGist() }
+ // )
+ }
+ const createNewFile = (parentFolder = 'browser') => {
+ // const self = this
+ // modalDialogCustom.prompt('Create new file', 'File Name (e.g Untitled.sol)', 'Untitled.sol', (input) => {
+ // if (!input) input = 'New file'
+ // helper.createNonClashingName(parentFolder + '/' + input, self.files, async (error, newName) => {
+ // if (error) return tooltip('Failed to create file ' + newName + ' ' + error)
+ // const fileManager = self._deps.fileManager
+ // const createFile = await fileManager.writeFile(newName, '')
+
+ // if (!createFile) {
+ // tooltip('Failed to create file ' + newName)
+ // } else {
+ // await fileManager.open(newName)
+ // if (newName.includes('_test.sol')) {
+ // self.events.trigger('newTestFileCreated', [newName])
+ // }
+ // }
+ // })
+ // }, null, true)
}
-}
-export const FileExplorer = (props: FileExplorerProps) => {
const [state, setState] = useState({
- files: props.files,
focusElement: null,
focusPath: null,
menuItems: [
@@ -51,34 +98,184 @@ export const FileExplorer = (props: FileExplorerProps) => {
title: 'Update the current [gist] explorer',
icon: 'fab fa-github'
}
- ].filter(item => props.menuItems && props.menuItems.find((name) => { return name === item.action }))
+ ].filter(item => props.menuItems && props.menuItems.find((name) => { return name === item.action })),
+ files: [],
+ actions: {
+ updateGist: () => {},
+ uploadFile,
+ publishToGist,
+ createNewFile
+ },
+ fileManager: null
})
useEffect(() => {
- if (props.files) {
- console.log('props.files.type: ', props.files.type)
- props.files.event.register('fileAdded', fileAdded)
- }
- }, [props.files])
+ const fileManager = registry.get('filemanager').api
+
+ setState(prevState => {
+ return { ...prevState, fileManager }
+ })
+ resolveDirectory(name)
+ }, [])
+
+ const resolveDirectory = (folderPath) => {
+ const folderIndex = state.files.findIndex(({ path }) => path === folderPath)
+
+ if (folderIndex === -1) {
+ files.resolveDirectory(folderPath, (error, fileTree) => {
+ if (error) console.error(error)
+ const files = normalize(folderPath, fileTree)
+
+ setState(prevState => {
+ return { ...prevState, files }
+ })
+ })
+ } else {
+ files.resolveDirectory(folderPath, (error, fileTree) => {
+ if (error) console.error(error)
+ const files = state.files
- const formatSelf = (key, data, li) => {
- const isFolder = !!data.children
+ files[folderIndex].child = normalize(folderPath, fileTree)
+ setState(prevState => {
+ return { ...prevState, files }
+ })
+ })
+ }
+ }
+ const label = (data) => {
return (
- {key.split('/').pop()}
+ { data.path.split('/').pop() }
)
}
+ const normalize = (path, filesList) => {
+ const prefix = path.split('/')[0]
+ const files = Object.keys(filesList).map(key => {
+ const path = prefix + '/' + key
+
+ return {
+ path,
+ name: extractNameFromKey(path),
+ isDirectory: filesList[key].isDirectory
+ }
+ })
+
+ return files
+ }
+
+ const extractNameFromKey = (key) => {
+ const keyPath = key.split('/')
+
+ return keyPath[keyPath.length - 1]
+ }
+
+ const toGist = (id) => {
+ // const proccedResult = function (error, data) {
+ // if (error) {
+ // modalDialogCustom.alert('Failed to manage gist: ' + error)
+ // console.log('Failed to manage gist: ' + error)
+ // } else {
+ // if (data.html_url) {
+ // modalDialogCustom.confirm('Gist is ready', `The gist is at ${data.html_url}. Would you like to open it in a new window?`, () => {
+ // window.open(data.html_url, '_blank')
+ // })
+ // } else {
+ // modalDialogCustom.alert(data.message + ' ' + data.documentation_url + ' ' + JSON.stringify(data.errors, null, '\t'))
+ // }
+ // }
+ // }
+
+ // /**
+ // * This function is to get the original content of given gist
+ // * @params id is the gist id to fetch
+ // */
+ // async function getOriginalFiles (id) {
+ // if (!id) {
+ // return []
+ // }
+
+ // const url = `https://api.github.com/gists/${id}`
+ // const res = await fetch(url)
+ // const data = await res.json()
+ // return data.files || []
+ // }
+
+ // // If 'id' is not defined, it is not a gist update but a creation so we have to take the files from the browser explorer.
+ // const folder = id ? 'browser/gists/' + id : 'browser/'
+ // this.packageFiles(this.files, folder, (error, packaged) => {
+ // if (error) {
+ // console.log(error)
+ // modalDialogCustom.alert('Failed to create gist: ' + error.message)
+ // } else {
+ // // check for token
+ // var tokenAccess = this._deps.config.get('settings/gist-access-token')
+ // if (!tokenAccess) {
+ // modalDialogCustom.alert(
+ // 'Remix requires an access token (which includes gists creation permission). Please go to the settings tab to create one.'
+ // )
+ // } else {
+ // const description = 'Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=' +
+ // queryParams.get().version + '&optimize=' + queryParams.get().optimize + '&runs=' + queryParams.get().runs + '&gist='
+ // const gists = new Gists({ token: tokenAccess })
+
+ // if (id) {
+ // const originalFileList = getOriginalFiles(id)
+ // // Telling the GIST API to remove files
+ // const updatedFileList = Object.keys(packaged)
+ // const allItems = Object.keys(originalFileList)
+ // .filter(fileName => updatedFileList.indexOf(fileName) === -1)
+ // .reduce((acc, deleteFileName) => ({
+ // ...acc,
+ // [deleteFileName]: null
+ // }), originalFileList)
+ // // adding new files
+ // updatedFileList.forEach((file) => {
+ // const _items = file.split('/')
+ // const _fileName = _items[_items.length - 1]
+ // allItems[_fileName] = packaged[file]
+ // })
+
+ // tooltip('Saving gist (' + id + ') ...')
+ // gists.edit({
+ // description: description,
+ // public: true,
+ // files: allItems,
+ // id: id
+ // }, (error, result) => {
+ // proccedResult(error, result)
+ // if (!error) {
+ // for (const key in allItems) {
+ // if (allItems[key] === null) delete allItems[key]
+ // }
+ // }
+ // })
+ // } else {
+ // // id is not existing, need to create a new gist
+ // tooltip('Creating a new gist ...')
+ // gists.create({
+ // description: description,
+ // public: true,
+ // files: packaged
+ // }, (error, result) => {
+ // proccedResult(error, result)
+ // })
+ // }
+ // }
+ // }
+ // })
+ }
+
// self._components = {}
// self._components.registry = localRegistry || globalRegistry
// self._deps = {
@@ -87,10 +284,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
// fileManager: self._components.registry.get('filemanager').api
// }
- // self.events.register('focus', function (path) {
- // self._deps.fileManager.open(path)
- // })
-
// self._components.registry.put({ api: self, name: `fileexplorer/${self.files.type}` })
// warn if file changed outside of Remix
@@ -128,33 +321,25 @@ export const FileExplorer = (props: FileExplorerProps) => {
// modalDialogCustom.alert(error)
// }
- const fileAdded = (filepath) => {
- const folderpath = filepath.split('/').slice(0, -1).join('/')
- console.log('filePath: ', folderpath)
- console.log('folderPath: ', folderpath)
- // const currentTree = self.treeView.nodeAt(folderpath)
- // if (!self.treeView.isExpanded(folderpath)) self.treeView.expand(folderpath)
- // if (currentTree) {
- // props.files.resolveDirectory(folderpath, (error, fileTree) => {
- // if (error) console.error(error)
- // if (!fileTree) return
- // fileTree = normalize(folderpath, fileTree)
- // self.treeView.updateNodeFromJSON(folderpath, fileTree, true)
- // self.focusElement = self.treeView.labelAt(self.focusPath)
- // // TODO: here we update the selected file (it applicable)
- // // cause we are refreshing the interface of the whole directory when there's a new file.
- // if (self.focusElement && !self.focusElement.classList.contains('bg-secondary')) {
- // self.focusElement.classList.add('bg-secondary')
- // }
- // })
- // }
- }
-
- const extractNameFromKey = (key) => {
- const keyPath = key.split('/')
-
- return keyPath[keyPath.length - 1]
- }
+ // const fileAdded = (filepath) => {
+ // const folderpath = filepath.split('/').slice(0, -1).join('/')
+ // const currentTree = self.treeView.nodeAt(folderpath)
+ // if (!self.treeView.isExpanded(folderpath)) self.treeView.expand(folderpath)
+ // if (currentTree) {
+ // props.files.resolveDirectory(folderpath, (error, fileTree) => {
+ // if (error) console.error(error)
+ // if (!fileTree) return
+ // fileTree = normalize(folderpath, fileTree)
+ // self.treeView.updateNodeFromJSON(folderpath, fileTree, true)
+ // self.focusElement = self.treeView.labelAt(self.focusPath)
+ // // TODO: here we update the selected file (it applicable)
+ // // cause we are refreshing the interface of the whole directory when there's a new file.
+ // if (self.focusElement && !self.focusElement.classList.contains('bg-secondary')) {
+ // self.focusElement.classList.add('bg-secondary')
+ // }
+ // })
+ // }
+ // }
const renderMenuItems = () => {
let items
@@ -181,7 +366,10 @@ export const FileExplorer = (props: FileExplorerProps) => {
{ stopPropagation(); this[action]() }}
+ onClick={(e) => {
+ e.stopPropagation()
+ state.actions[action]()
+ }}
className={'newFile ' + icon + ' remixui_newFile'}
title={title}
key={index}
@@ -193,57 +381,49 @@ export const FileExplorer = (props: FileExplorerProps) => {
}
return (
<>
- { props.name }
+ { name }
{items}
>
)
}
- 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
- // a file and then just use `files.add`. The file explorer will
- // pick that up via the 'fileAdded' event from the files module.
-
- ;[...target.files].forEach((file) => {
- const files = state.files
-
- function loadFile () {
- const fileReader = new FileReader()
-
- fileReader.onload = async function (event) {
- if (helper.checkSpecialChars(file.name)) {
- // modalDialogCustom.alert('Special characters are not allowed')
- return
+ const renderFiles = (file, index) => {
+ if (file.isDirectory) {
+ return (
+ { resolveDirectory(file.path) }} key={index} label={label(file)}>
+ {
+ file.child ? {
+ file.child.map((file, index) => {
+ return renderFiles(file, index)
+ })
+ }
+ :
}
- const success = await files.set(name, event.target.result)
-
- if (!success) {
- // modalDialogCustom.alert('Failed to create file ' + name)
- } else {
- // self.events.trigger('focus', [name])
- }
- }
- fileReader.readAsText(file)
- }
- const name = files.type + '/' + file.name
-
- files.exists(name, (error, exist) => {
- if (error) console.log(error)
- if (!exist) {
- loadFile()
- } else {
- // modalDialogCustom.confirm('Confirm overwrite', `The file ${name} already exists! Would you like to overwrite it?`, () => { loadFile() })
- }
- })
- })
+
+ )
+ } else {
+ return (
+ { state.fileManager.open(file.path) }}
+ />
+ )
+ }
}
return (
-
-
+
+
diff --git a/libs/remix-ui/file-explorer/src/lib/types/index.ts b/libs/remix-ui/file-explorer/src/lib/types/index.ts
index a2ee05aea6..75d7fb3dc9 100644
--- a/libs/remix-ui/file-explorer/src/lib/types/index.ts
+++ b/libs/remix-ui/file-explorer/src/lib/types/index.ts
@@ -1,7 +1,7 @@
/* eslint-disable-next-line */
export interface FileExplorerProps {
name: string,
- localRegistry: any,
+ registry: any,
files: any,
menuItems?: string[],
plugin: any
diff --git a/libs/remix-ui/tree-view/src/types/index.ts b/libs/remix-ui/tree-view/src/types/index.ts
index 9218469ec5..7ebbc05155 100644
--- a/libs/remix-ui/tree-view/src/types/index.ts
+++ b/libs/remix-ui/tree-view/src/types/index.ts
@@ -1,11 +1,11 @@
export interface TreeViewProps {
children?: React.ReactNode,
- id: string
+ id?: string
}
export interface TreeViewItemProps {
children?: React.ReactNode,
- id: string,
+ id?: string,
label: string | number | React.ReactNode,
expand?: boolean,
onClick?: VoidFunction,